blob: 70b7ae32a9fdf76c53170edc3b38f4c471ee2738 [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 Vlasenkob012b102007-02-19 22:43:01 +000056#include <paths.h>
57#include <setjmp.h>
58#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000059#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000060#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000061#endif
62
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000063#ifndef PIPE_BUF
64#define PIPE_BUF 4096 /* amount of buffering in a pipe */
65#endif
66
Denis Vlasenkob012b102007-02-19 22:43:01 +000067#if defined(__uClinux__)
68#error "Do not even bother, ash will not run on uClinux"
69#endif
70
Denis Vlasenkob012b102007-02-19 22:43:01 +000071
Denis Vlasenko01631112007-12-16 17:20:38 +000072/* ============ Hash table sizes. Configurable. */
73
74#define VTABSIZE 39
75#define ATABSIZE 39
76#define CMDTABLESIZE 31 /* should be prime */
77
78
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000079/* ============ Misc helpers */
80
81#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
82
83/* C99 say: "char" declaration may be signed or unsigned default */
84#define signed_char2int(sc) ((int)((signed char)sc))
85
86
Denis Vlasenkob012b102007-02-19 22:43:01 +000087/* ============ Shell options */
88
89static const char *const optletters_optnames[] = {
90 "e" "errexit",
91 "f" "noglob",
92 "I" "ignoreeof",
93 "i" "interactive",
94 "m" "monitor",
95 "n" "noexec",
96 "s" "stdin",
97 "x" "xtrace",
98 "v" "verbose",
99 "C" "noclobber",
100 "a" "allexport",
101 "b" "notify",
102 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000103 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000104#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000105 ,"\0" "nolog"
106 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000107#endif
108};
109
110#define optletters(n) optletters_optnames[(n)][0]
111#define optnames(n) (&optletters_optnames[(n)][1])
112
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000113enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114
Eric Andersenc470f442003-07-28 09:56:35 +0000115
Denis Vlasenkob012b102007-02-19 22:43:01 +0000116/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000117
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000118static const char homestr[] ALIGN1 = "HOME";
119static const char snlfmt[] ALIGN1 = "%s\n";
120static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000121
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000122/*
Eric Andersenc470f442003-07-28 09:56:35 +0000123 * We enclose jmp_buf in a structure so that we can declare pointers to
124 * jump locations. The global variable handler contains the location to
125 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000126 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000127 * exception handlers, the user should save the value of handler on entry
128 * to an inner scope, set handler to point to a jmploc structure for the
129 * inner scope, and restore handler on exit from the scope.
130 */
Eric Andersenc470f442003-07-28 09:56:35 +0000131struct jmploc {
132 jmp_buf loc;
133};
Denis Vlasenko01631112007-12-16 17:20:38 +0000134
135struct globals_misc {
136 /* pid of main shell */
137 int rootpid;
138 /* shell level: 0 for the main shell, 1 for its children, and so on */
139 int shlvl;
140#define rootshell (!shlvl)
141 char *minusc; /* argument to -c option */
142
143 char *curdir; // = nullstr; /* current working directory */
144 char *physdir; // = nullstr; /* physical working directory */
145
146 char *arg0; /* value of $0 */
147
148 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000149
150// disabled by vda: cannot understand how it was supposed to work -
151// cannot fix bugs. That's why you have to explain your non-trivial designs!
152// /* do we generate EXSIG events */
153// int exsig; /* counter */
154 volatile int suppressint; /* counter */
155 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
156 /* last pending signal */
157 volatile /*sig_atomic_t*/ smallint pendingsig;
158 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000159 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000160#define EXINT 0 /* SIGINT received */
161#define EXERROR 1 /* a generic error */
162#define EXSHELLPROC 2 /* execute a shell procedure */
163#define EXEXEC 3 /* command execution failed */
164#define EXEXIT 4 /* exit the shell */
165#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000166
Denis Vlasenko01631112007-12-16 17:20:38 +0000167 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000168 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000169
170 char optlist[NOPTS];
171#define eflag optlist[0]
172#define fflag optlist[1]
173#define Iflag optlist[2]
174#define iflag optlist[3]
175#define mflag optlist[4]
176#define nflag optlist[5]
177#define sflag optlist[6]
178#define xflag optlist[7]
179#define vflag optlist[8]
180#define Cflag optlist[9]
181#define aflag optlist[10]
182#define bflag optlist[11]
183#define uflag optlist[12]
184#define viflag optlist[13]
185#if DEBUG
186#define nolog optlist[14]
187#define debug optlist[15]
188#endif
189
190 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000191 /*
192 * Sigmode records the current value of the signal handlers for the various
193 * modes. A value of zero means that the current handler is not known.
194 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
195 */
196 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000197#define S_DFL 1 /* default signal handling (SIG_DFL) */
198#define S_CATCH 2 /* signal is caught */
199#define S_IGN 3 /* signal is ignored (SIG_IGN) */
200#define S_HARD_IGN 4 /* signal is ignored permenantly */
201#define S_RESET 5 /* temporary - to reset a hard ignored sig */
202
Denis Vlasenko01631112007-12-16 17:20:38 +0000203 /* indicates specified signal received */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +0000204 char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000205 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000206
207 /* Rarely referenced stuff */
208#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000209 /* Random number generators */
210 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
211 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000212#endif
213 pid_t backgndpid; /* pid of last background process */
214 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000215};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000216extern struct globals_misc *const ash_ptr_to_globals_misc;
217#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000218#define rootpid (G_misc.rootpid )
219#define shlvl (G_misc.shlvl )
220#define minusc (G_misc.minusc )
221#define curdir (G_misc.curdir )
222#define physdir (G_misc.physdir )
223#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000224#define exception_handler (G_misc.exception_handler)
225#define exception (G_misc.exception )
226#define suppressint (G_misc.suppressint )
227#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000228//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000229#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000230#define isloginsh (G_misc.isloginsh )
231#define nullstr (G_misc.nullstr )
232#define optlist (G_misc.optlist )
233#define sigmode (G_misc.sigmode )
234#define gotsig (G_misc.gotsig )
235#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000236#define random_galois_LFSR (G_misc.random_galois_LFSR)
237#define random_LCG (G_misc.random_LCG )
238#define backgndpid (G_misc.backgndpid )
239#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000240#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000241 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
242 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000243 curdir = nullstr; \
244 physdir = nullstr; \
245} while (0)
246
247
Denis Vlasenko559691a2008-10-05 18:39:31 +0000248/* ============ Utility functions */
249static int isdigit_str9(const char *str)
250{
251 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
252 while (--maxlen && isdigit(*str))
253 str++;
254 return (*str == '\0');
255}
Denis Vlasenko01631112007-12-16 17:20:38 +0000256
Denis Vlasenko559691a2008-10-05 18:39:31 +0000257
258/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000259/*
Eric Andersen2870d962001-07-02 17:27:21 +0000260 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000261 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000262 * much more efficient and portable. (But hacking the kernel is so much
263 * more fun than worrying about efficiency and portability. :-))
264 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000265#define INT_OFF do { \
266 suppressint++; \
267 xbarrier(); \
268} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000269
270/*
271 * Called to raise an exception. Since C doesn't include exceptions, we
272 * just do a longjmp to the exception handler. The type of exception is
273 * stored in the global variable "exception".
274 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000275static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000276static void
277raise_exception(int e)
278{
279#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000280 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000281 abort();
282#endif
283 INT_OFF;
284 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000285 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000286}
287
288/*
289 * Called from trap.c when a SIGINT is received. (If the user specifies
290 * that SIGINT is to be trapped or ignored using the trap builtin, then
291 * this routine is not called.) Suppressint is nonzero when interrupts
292 * are held using the INT_OFF macro. (The test for iflag is just
293 * defensive programming.)
294 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000295static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000296static void
297raise_interrupt(void)
298{
299 int i;
300
301 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000302 /* Signal is not automatically unmasked after it is raised,
303 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000304 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000305 /* pendingsig = 0; - now done in onsig() */
306
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307 i = EXSIG;
308 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
309 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000310 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000311 signal(SIGINT, SIG_DFL);
312 raise(SIGINT);
313 }
314 i = EXINT;
315 }
316 raise_exception(i);
317 /* NOTREACHED */
318}
319
320#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000321static void
322int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323{
324 if (--suppressint == 0 && intpending) {
325 raise_interrupt();
326 }
327}
328#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000329static void
330force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000331{
332 suppressint = 0;
333 if (intpending)
334 raise_interrupt();
335}
336#define FORCE_INT_ON force_int_on()
337#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000338#define INT_ON do { \
339 xbarrier(); \
340 if (--suppressint == 0 && intpending) \
341 raise_interrupt(); \
342} while (0)
343#define FORCE_INT_ON do { \
344 xbarrier(); \
345 suppressint = 0; \
346 if (intpending) \
347 raise_interrupt(); \
348} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000349#endif /* ASH_OPTIMIZE_FOR_SIZE */
350
351#define SAVE_INT(v) ((v) = suppressint)
352
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000353#define RESTORE_INT(v) do { \
354 xbarrier(); \
355 suppressint = (v); \
356 if (suppressint == 0 && intpending) \
357 raise_interrupt(); \
358} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000359
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000360/*
361 * Ignore a signal. Only one usage site - in forkchild()
362 */
363static void
364ignoresig(int signo)
365{
366 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
367 signal(signo, SIG_IGN);
368 }
369 sigmode[signo - 1] = S_HARD_IGN;
370}
371
372/*
373 * Signal handler. Only one usage site - in setsignal()
374 */
375static void
376onsig(int signo)
377{
378 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000379 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000380
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000381 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000382 if (!suppressint) {
383 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000384 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000385 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000386 intpending = 1;
387 }
388}
389
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000390
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000391/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000392
Eric Andersenc470f442003-07-28 09:56:35 +0000393static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000394outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000395{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000396 INT_OFF;
397 fputs(p, file);
398 INT_ON;
399}
400
401static void
402flush_stdout_stderr(void)
403{
404 INT_OFF;
405 fflush(stdout);
406 fflush(stderr);
407 INT_ON;
408}
409
410static void
411flush_stderr(void)
412{
413 INT_OFF;
414 fflush(stderr);
415 INT_ON;
416}
417
418static void
419outcslow(int c, FILE *dest)
420{
421 INT_OFF;
422 putc(c, dest);
423 fflush(dest);
424 INT_ON;
425}
426
427static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
428static int
429out1fmt(const char *fmt, ...)
430{
431 va_list ap;
432 int r;
433
434 INT_OFF;
435 va_start(ap, fmt);
436 r = vprintf(fmt, ap);
437 va_end(ap);
438 INT_ON;
439 return r;
440}
441
442static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
443static int
444fmtstr(char *outbuf, size_t length, const char *fmt, ...)
445{
446 va_list ap;
447 int ret;
448
449 va_start(ap, fmt);
450 INT_OFF;
451 ret = vsnprintf(outbuf, length, fmt, ap);
452 va_end(ap);
453 INT_ON;
454 return ret;
455}
456
457static void
458out1str(const char *p)
459{
460 outstr(p, stdout);
461}
462
463static void
464out2str(const char *p)
465{
466 outstr(p, stderr);
467 flush_stderr();
468}
469
470
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000471/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000472
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000473/* control characters in argument strings */
474#define CTLESC '\201' /* escape next character */
475#define CTLVAR '\202' /* variable defn */
476#define CTLENDVAR '\203'
477#define CTLBACKQ '\204'
478#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
479/* CTLBACKQ | CTLQUOTE == '\205' */
480#define CTLARI '\206' /* arithmetic expression */
481#define CTLENDARI '\207'
482#define CTLQUOTEMARK '\210'
483
484/* variable substitution byte (follows CTLVAR) */
485#define VSTYPE 0x0f /* type of variable substitution */
486#define VSNUL 0x10 /* colon--treat the empty string as unset */
487#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
488
489/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000490#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
491#define VSMINUS 0x2 /* ${var-text} */
492#define VSPLUS 0x3 /* ${var+text} */
493#define VSQUESTION 0x4 /* ${var?message} */
494#define VSASSIGN 0x5 /* ${var=text} */
495#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
496#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
497#define VSTRIMLEFT 0x8 /* ${var#pattern} */
498#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
499#define VSLENGTH 0xa /* ${#var} */
500#if ENABLE_ASH_BASH_COMPAT
501#define VSSUBSTR 0xc /* ${var:position:length} */
502#define VSREPLACE 0xd /* ${var/pattern/replacement} */
503#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
504#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000505
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000506static const char dolatstr[] ALIGN1 = {
507 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
508};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000509
Denis Vlasenko559691a2008-10-05 18:39:31 +0000510#define NCMD 0
511#define NPIPE 1
512#define NREDIR 2
513#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000514#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000515#define NAND 5
516#define NOR 6
517#define NSEMI 7
518#define NIF 8
519#define NWHILE 9
520#define NUNTIL 10
521#define NFOR 11
522#define NCASE 12
523#define NCLIST 13
524#define NDEFUN 14
525#define NARG 15
526#define NTO 16
527#if ENABLE_ASH_BASH_COMPAT
528#define NTO2 17
529#endif
530#define NCLOBBER 18
531#define NFROM 19
532#define NFROMTO 20
533#define NAPPEND 21
534#define NTOFD 22
535#define NFROMFD 23
536#define NHERE 24
537#define NXHERE 25
538#define NNOT 26
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000539
540union node;
541
542struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000543 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000544 union node *assign;
545 union node *args;
546 union node *redirect;
547};
548
549struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000550 smallint type;
551 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000552 struct nodelist *cmdlist;
553};
554
555struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000556 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000557 union node *n;
558 union node *redirect;
559};
560
561struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000562 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000563 union node *ch1;
564 union node *ch2;
565};
566
567struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000568 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000569 union node *test;
570 union node *ifpart;
571 union node *elsepart;
572};
573
574struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000575 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000576 union node *args;
577 union node *body;
578 char *var;
579};
580
581struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000582 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000583 union node *expr;
584 union node *cases;
585};
586
587struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000588 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000589 union node *next;
590 union node *pattern;
591 union node *body;
592};
593
594struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000595 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000596 union node *next;
597 char *text;
598 struct nodelist *backquote;
599};
600
Denis Vlasenko559691a2008-10-05 18:39:31 +0000601/* nfile and ndup layout must match!
602 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
603 * that it is actually NTO2 (>&file), and change its type.
604 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000605struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000606 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000607 union node *next;
608 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000609 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000610 union node *fname;
611 char *expfname;
612};
613
614struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000615 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000616 union node *next;
617 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000618 int dupfd;
619 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621};
622
623struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000624 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000625 union node *next;
626 int fd;
627 union node *doc;
628};
629
630struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000631 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000632 union node *com;
633};
634
635union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000636 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000637 struct ncmd ncmd;
638 struct npipe npipe;
639 struct nredir nredir;
640 struct nbinary nbinary;
641 struct nif nif;
642 struct nfor nfor;
643 struct ncase ncase;
644 struct nclist nclist;
645 struct narg narg;
646 struct nfile nfile;
647 struct ndup ndup;
648 struct nhere nhere;
649 struct nnot nnot;
650};
651
652struct nodelist {
653 struct nodelist *next;
654 union node *n;
655};
656
657struct funcnode {
658 int count;
659 union node n;
660};
661
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000662/*
663 * Free a parse tree.
664 */
665static void
666freefunc(struct funcnode *f)
667{
668 if (f && --f->count < 0)
669 free(f);
670}
671
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000672
673/* ============ Debugging output */
674
675#if DEBUG
676
677static FILE *tracefile;
678
679static void
680trace_printf(const char *fmt, ...)
681{
682 va_list va;
683
684 if (debug != 1)
685 return;
686 va_start(va, fmt);
687 vfprintf(tracefile, fmt, va);
688 va_end(va);
689}
690
691static void
692trace_vprintf(const char *fmt, va_list va)
693{
694 if (debug != 1)
695 return;
696 vfprintf(tracefile, fmt, va);
697}
698
699static void
700trace_puts(const char *s)
701{
702 if (debug != 1)
703 return;
704 fputs(s, tracefile);
705}
706
707static void
708trace_puts_quoted(char *s)
709{
710 char *p;
711 char c;
712
713 if (debug != 1)
714 return;
715 putc('"', tracefile);
716 for (p = s; *p; p++) {
717 switch (*p) {
718 case '\n': c = 'n'; goto backslash;
719 case '\t': c = 't'; goto backslash;
720 case '\r': c = 'r'; goto backslash;
721 case '"': c = '"'; goto backslash;
722 case '\\': c = '\\'; goto backslash;
723 case CTLESC: c = 'e'; goto backslash;
724 case CTLVAR: c = 'v'; goto backslash;
725 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
726 case CTLBACKQ: c = 'q'; goto backslash;
727 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
728 backslash:
729 putc('\\', tracefile);
730 putc(c, tracefile);
731 break;
732 default:
733 if (*p >= ' ' && *p <= '~')
734 putc(*p, tracefile);
735 else {
736 putc('\\', tracefile);
737 putc(*p >> 6 & 03, tracefile);
738 putc(*p >> 3 & 07, tracefile);
739 putc(*p & 07, tracefile);
740 }
741 break;
742 }
743 }
744 putc('"', tracefile);
745}
746
747static void
748trace_puts_args(char **ap)
749{
750 if (debug != 1)
751 return;
752 if (!*ap)
753 return;
754 while (1) {
755 trace_puts_quoted(*ap);
756 if (!*++ap) {
757 putc('\n', tracefile);
758 break;
759 }
760 putc(' ', tracefile);
761 }
762}
763
764static void
765opentrace(void)
766{
767 char s[100];
768#ifdef O_APPEND
769 int flags;
770#endif
771
772 if (debug != 1) {
773 if (tracefile)
774 fflush(tracefile);
775 /* leave open because libedit might be using it */
776 return;
777 }
778 strcpy(s, "./trace");
779 if (tracefile) {
780 if (!freopen(s, "a", tracefile)) {
781 fprintf(stderr, "Can't re-open %s\n", s);
782 debug = 0;
783 return;
784 }
785 } else {
786 tracefile = fopen(s, "a");
787 if (tracefile == NULL) {
788 fprintf(stderr, "Can't open %s\n", s);
789 debug = 0;
790 return;
791 }
792 }
793#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000794 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000795 if (flags >= 0)
796 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
797#endif
798 setlinebuf(tracefile);
799 fputs("\nTracing started.\n", tracefile);
800}
801
802static void
803indent(int amount, char *pfx, FILE *fp)
804{
805 int i;
806
807 for (i = 0; i < amount; i++) {
808 if (pfx && i == amount - 1)
809 fputs(pfx, fp);
810 putc('\t', fp);
811 }
812}
813
814/* little circular references here... */
815static void shtree(union node *n, int ind, char *pfx, FILE *fp);
816
817static void
818sharg(union node *arg, FILE *fp)
819{
820 char *p;
821 struct nodelist *bqlist;
822 int subtype;
823
824 if (arg->type != NARG) {
825 out1fmt("<node type %d>\n", arg->type);
826 abort();
827 }
828 bqlist = arg->narg.backquote;
829 for (p = arg->narg.text; *p; p++) {
830 switch (*p) {
831 case CTLESC:
832 putc(*++p, fp);
833 break;
834 case CTLVAR:
835 putc('$', fp);
836 putc('{', fp);
837 subtype = *++p;
838 if (subtype == VSLENGTH)
839 putc('#', fp);
840
841 while (*p != '=')
842 putc(*p++, fp);
843
844 if (subtype & VSNUL)
845 putc(':', fp);
846
847 switch (subtype & VSTYPE) {
848 case VSNORMAL:
849 putc('}', fp);
850 break;
851 case VSMINUS:
852 putc('-', fp);
853 break;
854 case VSPLUS:
855 putc('+', fp);
856 break;
857 case VSQUESTION:
858 putc('?', fp);
859 break;
860 case VSASSIGN:
861 putc('=', fp);
862 break;
863 case VSTRIMLEFT:
864 putc('#', fp);
865 break;
866 case VSTRIMLEFTMAX:
867 putc('#', fp);
868 putc('#', fp);
869 break;
870 case VSTRIMRIGHT:
871 putc('%', fp);
872 break;
873 case VSTRIMRIGHTMAX:
874 putc('%', fp);
875 putc('%', fp);
876 break;
877 case VSLENGTH:
878 break;
879 default:
880 out1fmt("<subtype %d>", subtype);
881 }
882 break;
883 case CTLENDVAR:
884 putc('}', fp);
885 break;
886 case CTLBACKQ:
887 case CTLBACKQ|CTLQUOTE:
888 putc('$', fp);
889 putc('(', fp);
890 shtree(bqlist->n, -1, NULL, fp);
891 putc(')', fp);
892 break;
893 default:
894 putc(*p, fp);
895 break;
896 }
897 }
898}
899
900static void
901shcmd(union node *cmd, FILE *fp)
902{
903 union node *np;
904 int first;
905 const char *s;
906 int dftfd;
907
908 first = 1;
909 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000910 if (!first)
911 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000912 sharg(np, fp);
913 first = 0;
914 }
915 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000916 if (!first)
917 putc(' ', fp);
918 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000919 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000920 case NTO: s = ">>"+1; dftfd = 1; break;
921 case NCLOBBER: s = ">|"; dftfd = 1; break;
922 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000923#if ENABLE_ASH_BASH_COMPAT
924 case NTO2:
925#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000926 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000927 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000928 case NFROMFD: s = "<&"; break;
929 case NFROMTO: s = "<>"; break;
930 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000931 }
932 if (np->nfile.fd != dftfd)
933 fprintf(fp, "%d", np->nfile.fd);
934 fputs(s, fp);
935 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
936 fprintf(fp, "%d", np->ndup.dupfd);
937 } else {
938 sharg(np->nfile.fname, fp);
939 }
940 first = 0;
941 }
942}
943
944static void
945shtree(union node *n, int ind, char *pfx, FILE *fp)
946{
947 struct nodelist *lp;
948 const char *s;
949
950 if (n == NULL)
951 return;
952
953 indent(ind, pfx, fp);
954 switch (n->type) {
955 case NSEMI:
956 s = "; ";
957 goto binop;
958 case NAND:
959 s = " && ";
960 goto binop;
961 case NOR:
962 s = " || ";
963 binop:
964 shtree(n->nbinary.ch1, ind, NULL, fp);
965 /* if (ind < 0) */
966 fputs(s, fp);
967 shtree(n->nbinary.ch2, ind, NULL, fp);
968 break;
969 case NCMD:
970 shcmd(n, fp);
971 if (ind >= 0)
972 putc('\n', fp);
973 break;
974 case NPIPE:
975 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
976 shcmd(lp->n, fp);
977 if (lp->next)
978 fputs(" | ", fp);
979 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000980 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000981 fputs(" &", fp);
982 if (ind >= 0)
983 putc('\n', fp);
984 break;
985 default:
986 fprintf(fp, "<node type %d>", n->type);
987 if (ind >= 0)
988 putc('\n', fp);
989 break;
990 }
991}
992
993static void
994showtree(union node *n)
995{
996 trace_puts("showtree called\n");
997 shtree(n, 1, NULL, stdout);
998}
999
1000#define TRACE(param) trace_printf param
1001#define TRACEV(param) trace_vprintf param
1002
1003#else
1004
1005#define TRACE(param)
1006#define TRACEV(param)
1007
1008#endif /* DEBUG */
1009
1010
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001011/* ============ Parser data */
1012
1013/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001014 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1015 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001016struct strlist {
1017 struct strlist *next;
1018 char *text;
1019};
1020
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001021struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001022
Denis Vlasenkob012b102007-02-19 22:43:01 +00001023struct strpush {
1024 struct strpush *prev; /* preceding string on stack */
1025 char *prevstring;
1026 int prevnleft;
1027#if ENABLE_ASH_ALIAS
1028 struct alias *ap; /* if push was associated with an alias */
1029#endif
1030 char *string; /* remember the string since it may change */
1031};
1032
1033struct parsefile {
1034 struct parsefile *prev; /* preceding file on stack */
1035 int linno; /* current line */
1036 int fd; /* file descriptor (or -1 if string) */
1037 int nleft; /* number of chars left in this line */
1038 int lleft; /* number of chars left in this buffer */
1039 char *nextc; /* next char in buffer */
1040 char *buf; /* input buffer */
1041 struct strpush *strpush; /* for pushing strings at this level */
1042 struct strpush basestrpush; /* so pushing one is fast */
1043};
1044
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001045static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001046static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001047static int startlinno; /* line # where last token started */
1048static char *commandname; /* currently executing command */
1049static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001050static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001051
1052
1053/* ============ Message printing */
1054
1055static void
1056ash_vmsg(const char *msg, va_list ap)
1057{
1058 fprintf(stderr, "%s: ", arg0);
1059 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001060 if (strcmp(arg0, commandname))
1061 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001062 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001063 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001064 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001065 vfprintf(stderr, msg, ap);
1066 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001067}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001068
1069/*
1070 * Exverror is called to raise the error exception. If the second argument
1071 * is not NULL then error prints an error message using printf style
1072 * formatting. It then raises the error exception.
1073 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001074static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001075static void
1076ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001077{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001078#if DEBUG
1079 if (msg) {
1080 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1081 TRACEV((msg, ap));
1082 TRACE(("\") pid=%d\n", getpid()));
1083 } else
1084 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1085 if (msg)
1086#endif
1087 ash_vmsg(msg, ap);
1088
1089 flush_stdout_stderr();
1090 raise_exception(cond);
1091 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001092}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001093
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001094static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001095static void
1096ash_msg_and_raise_error(const char *msg, ...)
1097{
1098 va_list ap;
1099
1100 va_start(ap, msg);
1101 ash_vmsg_and_raise(EXERROR, msg, ap);
1102 /* NOTREACHED */
1103 va_end(ap);
1104}
1105
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001106static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001107static void
1108ash_msg_and_raise(int cond, const char *msg, ...)
1109{
1110 va_list ap;
1111
1112 va_start(ap, msg);
1113 ash_vmsg_and_raise(cond, msg, ap);
1114 /* NOTREACHED */
1115 va_end(ap);
1116}
1117
1118/*
1119 * error/warning routines for external builtins
1120 */
1121static void
1122ash_msg(const char *fmt, ...)
1123{
1124 va_list ap;
1125
1126 va_start(ap, fmt);
1127 ash_vmsg(fmt, ap);
1128 va_end(ap);
1129}
1130
1131/*
1132 * Return a string describing an error. The returned string may be a
1133 * pointer to a static buffer that will be overwritten on the next call.
1134 * Action describes the operation that got the error.
1135 */
1136static const char *
1137errmsg(int e, const char *em)
1138{
1139 if (e == ENOENT || e == ENOTDIR) {
1140 return em;
1141 }
1142 return strerror(e);
1143}
1144
1145
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001146/* ============ Memory allocation */
1147
1148/*
1149 * It appears that grabstackstr() will barf with such alignments
1150 * because stalloc() will return a string allocated in a new stackblock.
1151 */
1152#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1153enum {
1154 /* Most machines require the value returned from malloc to be aligned
1155 * in some way. The following macro will get this right
1156 * on many machines. */
1157 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1158 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001159 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001160};
1161
1162struct stack_block {
1163 struct stack_block *prev;
1164 char space[MINSIZE];
1165};
1166
1167struct stackmark {
1168 struct stack_block *stackp;
1169 char *stacknxt;
1170 size_t stacknleft;
1171 struct stackmark *marknext;
1172};
1173
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001174
Denis Vlasenko01631112007-12-16 17:20:38 +00001175struct globals_memstack {
1176 struct stack_block *g_stackp; // = &stackbase;
1177 struct stackmark *markp;
1178 char *g_stacknxt; // = stackbase.space;
1179 char *sstrend; // = stackbase.space + MINSIZE;
1180 size_t g_stacknleft; // = MINSIZE;
1181 int herefd; // = -1;
1182 struct stack_block stackbase;
1183};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001184extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1185#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001186#define g_stackp (G_memstack.g_stackp )
1187#define markp (G_memstack.markp )
1188#define g_stacknxt (G_memstack.g_stacknxt )
1189#define sstrend (G_memstack.sstrend )
1190#define g_stacknleft (G_memstack.g_stacknleft)
1191#define herefd (G_memstack.herefd )
1192#define stackbase (G_memstack.stackbase )
1193#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001194 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1195 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001196 g_stackp = &stackbase; \
1197 g_stacknxt = stackbase.space; \
1198 g_stacknleft = MINSIZE; \
1199 sstrend = stackbase.space + MINSIZE; \
1200 herefd = -1; \
1201} while (0)
1202
1203#define stackblock() ((void *)g_stacknxt)
1204#define stackblocksize() g_stacknleft
1205
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001206
1207static void *
1208ckrealloc(void * p, size_t nbytes)
1209{
1210 p = realloc(p, nbytes);
1211 if (!p)
1212 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1213 return p;
1214}
1215
1216static void *
1217ckmalloc(size_t nbytes)
1218{
1219 return ckrealloc(NULL, nbytes);
1220}
1221
Denis Vlasenko597906c2008-02-20 16:38:54 +00001222static void *
1223ckzalloc(size_t nbytes)
1224{
1225 return memset(ckmalloc(nbytes), 0, nbytes);
1226}
1227
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001228/*
1229 * Make a copy of a string in safe storage.
1230 */
1231static char *
1232ckstrdup(const char *s)
1233{
1234 char *p = strdup(s);
1235 if (!p)
1236 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1237 return p;
1238}
1239
1240/*
1241 * Parse trees for commands are allocated in lifo order, so we use a stack
1242 * to make this more efficient, and also to avoid all sorts of exception
1243 * handling code to handle interrupts in the middle of a parse.
1244 *
1245 * The size 504 was chosen because the Ultrix malloc handles that size
1246 * well.
1247 */
1248static void *
1249stalloc(size_t nbytes)
1250{
1251 char *p;
1252 size_t aligned;
1253
1254 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001255 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001256 size_t len;
1257 size_t blocksize;
1258 struct stack_block *sp;
1259
1260 blocksize = aligned;
1261 if (blocksize < MINSIZE)
1262 blocksize = MINSIZE;
1263 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1264 if (len < blocksize)
1265 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1266 INT_OFF;
1267 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001268 sp->prev = g_stackp;
1269 g_stacknxt = sp->space;
1270 g_stacknleft = blocksize;
1271 sstrend = g_stacknxt + blocksize;
1272 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001273 INT_ON;
1274 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001275 p = g_stacknxt;
1276 g_stacknxt += aligned;
1277 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001278 return p;
1279}
1280
Denis Vlasenko597906c2008-02-20 16:38:54 +00001281static void *
1282stzalloc(size_t nbytes)
1283{
1284 return memset(stalloc(nbytes), 0, nbytes);
1285}
1286
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001287static void
1288stunalloc(void *p)
1289{
1290#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001291 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001292 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001293 abort();
1294 }
1295#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001296 g_stacknleft += g_stacknxt - (char *)p;
1297 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001298}
1299
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001300/*
1301 * Like strdup but works with the ash stack.
1302 */
1303static char *
1304ststrdup(const char *p)
1305{
1306 size_t len = strlen(p) + 1;
1307 return memcpy(stalloc(len), p, len);
1308}
1309
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001310static void
1311setstackmark(struct stackmark *mark)
1312{
Denis Vlasenko01631112007-12-16 17:20:38 +00001313 mark->stackp = g_stackp;
1314 mark->stacknxt = g_stacknxt;
1315 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001316 mark->marknext = markp;
1317 markp = mark;
1318}
1319
1320static void
1321popstackmark(struct stackmark *mark)
1322{
1323 struct stack_block *sp;
1324
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001325 if (!mark->stackp)
1326 return;
1327
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001328 INT_OFF;
1329 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001330 while (g_stackp != mark->stackp) {
1331 sp = g_stackp;
1332 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001333 free(sp);
1334 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001335 g_stacknxt = mark->stacknxt;
1336 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001337 sstrend = mark->stacknxt + mark->stacknleft;
1338 INT_ON;
1339}
1340
1341/*
1342 * When the parser reads in a string, it wants to stick the string on the
1343 * stack and only adjust the stack pointer when it knows how big the
1344 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1345 * of space on top of the stack and stackblocklen returns the length of
1346 * this block. Growstackblock will grow this space by at least one byte,
1347 * possibly moving it (like realloc). Grabstackblock actually allocates the
1348 * part of the block that has been used.
1349 */
1350static void
1351growstackblock(void)
1352{
1353 size_t newlen;
1354
Denis Vlasenko01631112007-12-16 17:20:38 +00001355 newlen = g_stacknleft * 2;
1356 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001357 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1358 if (newlen < 128)
1359 newlen += 128;
1360
Denis Vlasenko01631112007-12-16 17:20:38 +00001361 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001362 struct stack_block *oldstackp;
1363 struct stackmark *xmark;
1364 struct stack_block *sp;
1365 struct stack_block *prevstackp;
1366 size_t grosslen;
1367
1368 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001369 oldstackp = g_stackp;
1370 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001371 prevstackp = sp->prev;
1372 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1373 sp = ckrealloc(sp, grosslen);
1374 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001375 g_stackp = sp;
1376 g_stacknxt = sp->space;
1377 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001378 sstrend = sp->space + newlen;
1379
1380 /*
1381 * Stack marks pointing to the start of the old block
1382 * must be relocated to point to the new block
1383 */
1384 xmark = markp;
1385 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001386 xmark->stackp = g_stackp;
1387 xmark->stacknxt = g_stacknxt;
1388 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001389 xmark = xmark->marknext;
1390 }
1391 INT_ON;
1392 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001393 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001394 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001395 char *p = stalloc(newlen);
1396
1397 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001398 g_stacknxt = memcpy(p, oldspace, oldlen);
1399 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001400 }
1401}
1402
1403static void
1404grabstackblock(size_t len)
1405{
1406 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001407 g_stacknxt += len;
1408 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001409}
1410
1411/*
1412 * The following routines are somewhat easier to use than the above.
1413 * The user declares a variable of type STACKSTR, which may be declared
1414 * to be a register. The macro STARTSTACKSTR initializes things. Then
1415 * the user uses the macro STPUTC to add characters to the string. In
1416 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1417 * grown as necessary. When the user is done, she can just leave the
1418 * string there and refer to it using stackblock(). Or she can allocate
1419 * the space for it using grabstackstr(). If it is necessary to allow
1420 * someone else to use the stack temporarily and then continue to grow
1421 * the string, the user should use grabstack to allocate the space, and
1422 * then call ungrabstr(p) to return to the previous mode of operation.
1423 *
1424 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1425 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1426 * is space for at least one character.
1427 */
1428static void *
1429growstackstr(void)
1430{
1431 size_t len = stackblocksize();
1432 if (herefd >= 0 && len >= 1024) {
1433 full_write(herefd, stackblock(), len);
1434 return stackblock();
1435 }
1436 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001437 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001438}
1439
1440/*
1441 * Called from CHECKSTRSPACE.
1442 */
1443static char *
1444makestrspace(size_t newlen, char *p)
1445{
Denis Vlasenko01631112007-12-16 17:20:38 +00001446 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001447 size_t size = stackblocksize();
1448
1449 for (;;) {
1450 size_t nleft;
1451
1452 size = stackblocksize();
1453 nleft = size - len;
1454 if (nleft >= newlen)
1455 break;
1456 growstackblock();
1457 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001458 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001459}
1460
1461static char *
1462stack_nputstr(const char *s, size_t n, char *p)
1463{
1464 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001465 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001466 return p;
1467}
1468
1469static char *
1470stack_putstr(const char *s, char *p)
1471{
1472 return stack_nputstr(s, strlen(s), p);
1473}
1474
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001475static char *
1476_STPUTC(int c, char *p)
1477{
1478 if (p == sstrend)
1479 p = growstackstr();
1480 *p++ = c;
1481 return p;
1482}
1483
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001484#define STARTSTACKSTR(p) ((p) = stackblock())
1485#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001486#define CHECKSTRSPACE(n, p) do { \
1487 char *q = (p); \
1488 size_t l = (n); \
1489 size_t m = sstrend - q; \
1490 if (l > m) \
1491 (p) = makestrspace(l, q); \
1492} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001493#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001494#define STACKSTRNUL(p) do { \
1495 if ((p) == sstrend) \
1496 (p) = growstackstr(); \
1497 *(p) = '\0'; \
1498} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001499#define STUNPUTC(p) (--(p))
1500#define STTOPC(p) ((p)[-1])
1501#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001502
1503#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001504#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001505#define stackstrend() ((void *)sstrend)
1506
1507
1508/* ============ String helpers */
1509
1510/*
1511 * prefix -- see if pfx is a prefix of string.
1512 */
1513static char *
1514prefix(const char *string, const char *pfx)
1515{
1516 while (*pfx) {
1517 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001518 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001519 }
1520 return (char *) string;
1521}
1522
1523/*
1524 * Check for a valid number. This should be elsewhere.
1525 */
1526static int
1527is_number(const char *p)
1528{
1529 do {
1530 if (!isdigit(*p))
1531 return 0;
1532 } while (*++p != '\0');
1533 return 1;
1534}
1535
1536/*
1537 * Convert a string of digits to an integer, printing an error message on
1538 * failure.
1539 */
1540static int
1541number(const char *s)
1542{
1543 if (!is_number(s))
1544 ash_msg_and_raise_error(illnum, s);
1545 return atoi(s);
1546}
1547
1548/*
1549 * Produce a possibly single quoted string suitable as input to the shell.
1550 * The return string is allocated on the stack.
1551 */
1552static char *
1553single_quote(const char *s)
1554{
1555 char *p;
1556
1557 STARTSTACKSTR(p);
1558
1559 do {
1560 char *q;
1561 size_t len;
1562
1563 len = strchrnul(s, '\'') - s;
1564
1565 q = p = makestrspace(len + 3, p);
1566
1567 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001568 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001569 *q++ = '\'';
1570 s += len;
1571
1572 STADJUST(q - p, p);
1573
1574 len = strspn(s, "'");
1575 if (!len)
1576 break;
1577
1578 q = p = makestrspace(len + 3, p);
1579
1580 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001581 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001582 *q++ = '"';
1583 s += len;
1584
1585 STADJUST(q - p, p);
1586 } while (*s);
1587
1588 USTPUTC(0, p);
1589
1590 return stackblock();
1591}
1592
1593
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001594/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001595
1596static char **argptr; /* argument list for builtin commands */
1597static char *optionarg; /* set by nextopt (like getopt) */
1598static char *optptr; /* used by nextopt */
1599
1600/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001601 * XXX - should get rid of. Have all builtins use getopt(3).
1602 * The library getopt must have the BSD extension static variable
1603 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001604 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001605 * Standard option processing (a la getopt) for builtin routines.
1606 * The only argument that is passed to nextopt is the option string;
1607 * the other arguments are unnecessary. It returns the character,
1608 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001609 */
1610static int
1611nextopt(const char *optstring)
1612{
1613 char *p;
1614 const char *q;
1615 char c;
1616
1617 p = optptr;
1618 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001619 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001620 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001621 if (p == NULL)
1622 return '\0';
1623 if (*p != '-')
1624 return '\0';
1625 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001626 return '\0';
1627 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001628 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001629 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001630 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001631 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001632 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001633 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001634 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001635 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001636 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001637 if (*++q == ':')
1638 q++;
1639 }
1640 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001641 if (*p == '\0') {
1642 p = *argptr++;
1643 if (p == NULL)
1644 ash_msg_and_raise_error("no arg for -%c option", c);
1645 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001646 optionarg = p;
1647 p = NULL;
1648 }
1649 optptr = p;
1650 return c;
1651}
1652
1653
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001654/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001655
Denis Vlasenko01631112007-12-16 17:20:38 +00001656/*
1657 * The parsefile structure pointed to by the global variable parsefile
1658 * contains information about the current file being read.
1659 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001660struct shparam {
1661 int nparam; /* # of positional parameters (without $0) */
1662#if ENABLE_ASH_GETOPTS
1663 int optind; /* next parameter to be processed by getopts */
1664 int optoff; /* used by getopts */
1665#endif
1666 unsigned char malloced; /* if parameter list dynamically allocated */
1667 char **p; /* parameter list */
1668};
1669
1670/*
1671 * Free the list of positional parameters.
1672 */
1673static void
1674freeparam(volatile struct shparam *param)
1675{
Denis Vlasenko01631112007-12-16 17:20:38 +00001676 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001677 char **ap, **ap1;
1678 ap = ap1 = param->p;
1679 while (*ap)
1680 free(*ap++);
1681 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001682 }
1683}
1684
1685#if ENABLE_ASH_GETOPTS
1686static void getoptsreset(const char *value);
1687#endif
1688
1689struct var {
1690 struct var *next; /* next entry in hash list */
1691 int flags; /* flags are defined above */
1692 const char *text; /* name=value */
1693 void (*func)(const char *); /* function to be called when */
1694 /* the variable gets set/unset */
1695};
1696
1697struct localvar {
1698 struct localvar *next; /* next local variable in list */
1699 struct var *vp; /* the variable that was made local */
1700 int flags; /* saved flags */
1701 const char *text; /* saved text */
1702};
1703
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001704/* flags */
1705#define VEXPORT 0x01 /* variable is exported */
1706#define VREADONLY 0x02 /* variable cannot be modified */
1707#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1708#define VTEXTFIXED 0x08 /* text is statically allocated */
1709#define VSTACK 0x10 /* text is allocated on the stack */
1710#define VUNSET 0x20 /* the variable is not set */
1711#define VNOFUNC 0x40 /* don't call the callback function */
1712#define VNOSET 0x80 /* do not set variable - just readonly test */
1713#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001714#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001715# define VDYNAMIC 0x200 /* dynamic variable */
1716#else
1717# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001718#endif
1719
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001720#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001721static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001722#define defifs (defifsvar + 4)
1723#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001724static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001725#endif
1726
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001727
Denis Vlasenko01631112007-12-16 17:20:38 +00001728/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001729#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001730static void
1731change_lc_all(const char *value)
1732{
1733 if (value && *value != '\0')
1734 setlocale(LC_ALL, value);
1735}
1736static void
1737change_lc_ctype(const char *value)
1738{
1739 if (value && *value != '\0')
1740 setlocale(LC_CTYPE, value);
1741}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001742#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743#if ENABLE_ASH_MAIL
1744static void chkmail(void);
1745static void changemail(const char *);
1746#endif
1747static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#if ENABLE_ASH_RANDOM_SUPPORT
1749static void change_random(const char *);
1750#endif
1751
Denis Vlasenko01631112007-12-16 17:20:38 +00001752static const struct {
1753 int flags;
1754 const char *text;
1755 void (*func)(const char *);
1756} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001758 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001760 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001761#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001763 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1764 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001766 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1767 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1768 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1769 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001770#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001771 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#endif
1773#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001774 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001775#endif
1776#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001777 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1778 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001779#endif
1780#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001781 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001782#endif
1783};
1784
Denis Vlasenko0b769642008-07-24 07:54:57 +00001785struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001786
1787struct globals_var {
1788 struct shparam shellparam; /* $@ current positional parameters */
1789 struct redirtab *redirlist;
1790 int g_nullredirs;
1791 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1792 struct var *vartab[VTABSIZE];
1793 struct var varinit[ARRAY_SIZE(varinit_data)];
1794};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001795extern struct globals_var *const ash_ptr_to_globals_var;
1796#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001797#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001798//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001799#define g_nullredirs (G_var.g_nullredirs )
1800#define preverrout_fd (G_var.preverrout_fd)
1801#define vartab (G_var.vartab )
1802#define varinit (G_var.varinit )
1803#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001804 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001805 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1806 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001807 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1808 varinit[i].flags = varinit_data[i].flags; \
1809 varinit[i].text = varinit_data[i].text; \
1810 varinit[i].func = varinit_data[i].func; \
1811 } \
1812} while (0)
1813
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001814#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001815#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001816# define vmail (&vifs)[1]
1817# define vmpath (&vmail)[1]
1818# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001819#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001820# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001821#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001822#define vps1 (&vpath)[1]
1823#define vps2 (&vps1)[1]
1824#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001825#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001826# define voptind (&vps4)[1]
1827# if ENABLE_ASH_RANDOM_SUPPORT
1828# define vrandom (&voptind)[1]
1829# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001831# if ENABLE_ASH_RANDOM_SUPPORT
1832# define vrandom (&vps4)[1]
1833# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001834#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001835
1836/*
1837 * The following macros access the values of the above variables.
1838 * They have to skip over the name. They return the null string
1839 * for unset variables.
1840 */
1841#define ifsval() (vifs.text + 4)
1842#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001843#if ENABLE_ASH_MAIL
1844# define mailval() (vmail.text + 5)
1845# define mpathval() (vmpath.text + 9)
1846# define mpathset() ((vmpath.flags & VUNSET) == 0)
1847#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001848#define pathval() (vpath.text + 5)
1849#define ps1val() (vps1.text + 4)
1850#define ps2val() (vps2.text + 4)
1851#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001852#if ENABLE_ASH_GETOPTS
1853# define optindval() (voptind.text + 7)
1854#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001855
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001856
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001857#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1858#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1859
Denis Vlasenko01631112007-12-16 17:20:38 +00001860#if ENABLE_ASH_GETOPTS
1861static void
1862getoptsreset(const char *value)
1863{
1864 shellparam.optind = number(value);
1865 shellparam.optoff = -1;
1866}
1867#endif
1868
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001869/*
1870 * Return of a legal variable name (a letter or underscore followed by zero or
1871 * more letters, underscores, and digits).
1872 */
1873static char *
1874endofname(const char *name)
1875{
1876 char *p;
1877
1878 p = (char *) name;
1879 if (!is_name(*p))
1880 return p;
1881 while (*++p) {
1882 if (!is_in_name(*p))
1883 break;
1884 }
1885 return p;
1886}
1887
1888/*
1889 * Compares two strings up to the first = or '\0'. The first
1890 * string must be terminated by '='; the second may be terminated by
1891 * either '=' or '\0'.
1892 */
1893static int
1894varcmp(const char *p, const char *q)
1895{
1896 int c, d;
1897
1898 while ((c = *p) == (d = *q)) {
1899 if (!c || c == '=')
1900 goto out;
1901 p++;
1902 q++;
1903 }
1904 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001905 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001906 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001907 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001908 out:
1909 return c - d;
1910}
1911
1912static int
1913varequal(const char *a, const char *b)
1914{
1915 return !varcmp(a, b);
1916}
1917
1918/*
1919 * Find the appropriate entry in the hash table from the name.
1920 */
1921static struct var **
1922hashvar(const char *p)
1923{
1924 unsigned hashval;
1925
1926 hashval = ((unsigned char) *p) << 4;
1927 while (*p && *p != '=')
1928 hashval += (unsigned char) *p++;
1929 return &vartab[hashval % VTABSIZE];
1930}
1931
1932static int
1933vpcmp(const void *a, const void *b)
1934{
1935 return varcmp(*(const char **)a, *(const char **)b);
1936}
1937
1938/*
1939 * This routine initializes the builtin variables.
1940 */
1941static void
1942initvar(void)
1943{
1944 struct var *vp;
1945 struct var *end;
1946 struct var **vpp;
1947
1948 /*
1949 * PS1 depends on uid
1950 */
1951#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1952 vps1.text = "PS1=\\w \\$ ";
1953#else
1954 if (!geteuid())
1955 vps1.text = "PS1=# ";
1956#endif
1957 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001958 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001959 do {
1960 vpp = hashvar(vp->text);
1961 vp->next = *vpp;
1962 *vpp = vp;
1963 } while (++vp < end);
1964}
1965
1966static struct var **
1967findvar(struct var **vpp, const char *name)
1968{
1969 for (; *vpp; vpp = &(*vpp)->next) {
1970 if (varequal((*vpp)->text, name)) {
1971 break;
1972 }
1973 }
1974 return vpp;
1975}
1976
1977/*
1978 * Find the value of a variable. Returns NULL if not set.
1979 */
1980static char *
1981lookupvar(const char *name)
1982{
1983 struct var *v;
1984
1985 v = *findvar(hashvar(name), name);
1986 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001987#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001988 /*
1989 * Dynamic variables are implemented roughly the same way they are
1990 * in bash. Namely, they're "special" so long as they aren't unset.
1991 * As soon as they're unset, they're no longer dynamic, and dynamic
1992 * lookup will no longer happen at that point. -- PFM.
1993 */
1994 if ((v->flags & VDYNAMIC))
1995 (*v->func)(NULL);
1996#endif
1997 if (!(v->flags & VUNSET))
1998 return strchrnul(v->text, '=') + 1;
1999 }
2000 return NULL;
2001}
2002
2003/*
2004 * Search the environment of a builtin command.
2005 */
2006static char *
2007bltinlookup(const char *name)
2008{
2009 struct strlist *sp;
2010
2011 for (sp = cmdenviron; sp; sp = sp->next) {
2012 if (varequal(sp->text, name))
2013 return strchrnul(sp->text, '=') + 1;
2014 }
2015 return lookupvar(name);
2016}
2017
2018/*
2019 * Same as setvar except that the variable and value are passed in
2020 * the first argument as name=value. Since the first argument will
2021 * be actually stored in the table, it should not be a string that
2022 * will go away.
2023 * Called with interrupts off.
2024 */
2025static void
2026setvareq(char *s, int flags)
2027{
2028 struct var *vp, **vpp;
2029
2030 vpp = hashvar(s);
2031 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2032 vp = *findvar(vpp, s);
2033 if (vp) {
2034 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2035 const char *n;
2036
2037 if (flags & VNOSAVE)
2038 free(s);
2039 n = vp->text;
2040 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2041 }
2042
2043 if (flags & VNOSET)
2044 return;
2045
2046 if (vp->func && (flags & VNOFUNC) == 0)
2047 (*vp->func)(strchrnul(s, '=') + 1);
2048
2049 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2050 free((char*)vp->text);
2051
2052 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2053 } else {
2054 if (flags & VNOSET)
2055 return;
2056 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002057 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002058 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002059 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002060 *vpp = vp;
2061 }
2062 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2063 s = ckstrdup(s);
2064 vp->text = s;
2065 vp->flags = flags;
2066}
2067
2068/*
2069 * Set the value of a variable. The flags argument is ored with the
2070 * flags of the variable. If val is NULL, the variable is unset.
2071 */
2072static void
2073setvar(const char *name, const char *val, int flags)
2074{
2075 char *p, *q;
2076 size_t namelen;
2077 char *nameeq;
2078 size_t vallen;
2079
2080 q = endofname(name);
2081 p = strchrnul(q, '=');
2082 namelen = p - name;
2083 if (!namelen || p != q)
2084 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2085 vallen = 0;
2086 if (val == NULL) {
2087 flags |= VUNSET;
2088 } else {
2089 vallen = strlen(val);
2090 }
2091 INT_OFF;
2092 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002093 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094 if (val) {
2095 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002096 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002097 }
2098 *p = '\0';
2099 setvareq(nameeq, flags | VNOSAVE);
2100 INT_ON;
2101}
2102
2103#if ENABLE_ASH_GETOPTS
2104/*
2105 * Safe version of setvar, returns 1 on success 0 on failure.
2106 */
2107static int
2108setvarsafe(const char *name, const char *val, int flags)
2109{
2110 int err;
2111 volatile int saveint;
2112 struct jmploc *volatile savehandler = exception_handler;
2113 struct jmploc jmploc;
2114
2115 SAVE_INT(saveint);
2116 if (setjmp(jmploc.loc))
2117 err = 1;
2118 else {
2119 exception_handler = &jmploc;
2120 setvar(name, val, flags);
2121 err = 0;
2122 }
2123 exception_handler = savehandler;
2124 RESTORE_INT(saveint);
2125 return err;
2126}
2127#endif
2128
2129/*
2130 * Unset the specified variable.
2131 */
2132static int
2133unsetvar(const char *s)
2134{
2135 struct var **vpp;
2136 struct var *vp;
2137 int retval;
2138
2139 vpp = findvar(hashvar(s), s);
2140 vp = *vpp;
2141 retval = 2;
2142 if (vp) {
2143 int flags = vp->flags;
2144
2145 retval = 1;
2146 if (flags & VREADONLY)
2147 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002148#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002149 vp->flags &= ~VDYNAMIC;
2150#endif
2151 if (flags & VUNSET)
2152 goto ok;
2153 if ((flags & VSTRFIXED) == 0) {
2154 INT_OFF;
2155 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2156 free((char*)vp->text);
2157 *vpp = vp->next;
2158 free(vp);
2159 INT_ON;
2160 } else {
2161 setvar(s, 0, 0);
2162 vp->flags &= ~VEXPORT;
2163 }
2164 ok:
2165 retval = 0;
2166 }
2167 out:
2168 return retval;
2169}
2170
2171/*
2172 * Process a linked list of variable assignments.
2173 */
2174static void
2175listsetvar(struct strlist *list_set_var, int flags)
2176{
2177 struct strlist *lp = list_set_var;
2178
2179 if (!lp)
2180 return;
2181 INT_OFF;
2182 do {
2183 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002184 lp = lp->next;
2185 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186 INT_ON;
2187}
2188
2189/*
2190 * Generate a list of variables satisfying the given conditions.
2191 */
2192static char **
2193listvars(int on, int off, char ***end)
2194{
2195 struct var **vpp;
2196 struct var *vp;
2197 char **ep;
2198 int mask;
2199
2200 STARTSTACKSTR(ep);
2201 vpp = vartab;
2202 mask = on | off;
2203 do {
2204 for (vp = *vpp; vp; vp = vp->next) {
2205 if ((vp->flags & mask) == on) {
2206 if (ep == stackstrend())
2207 ep = growstackstr();
2208 *ep++ = (char *) vp->text;
2209 }
2210 }
2211 } while (++vpp < vartab + VTABSIZE);
2212 if (ep == stackstrend())
2213 ep = growstackstr();
2214 if (end)
2215 *end = ep;
2216 *ep++ = NULL;
2217 return grabstackstr(ep);
2218}
2219
2220
2221/* ============ Path search helper
2222 *
2223 * The variable path (passed by reference) should be set to the start
2224 * of the path before the first call; padvance will update
2225 * this value as it proceeds. Successive calls to padvance will return
2226 * the possible path expansions in sequence. If an option (indicated by
2227 * a percent sign) appears in the path entry then the global variable
2228 * pathopt will be set to point to it; otherwise pathopt will be set to
2229 * NULL.
2230 */
2231static const char *pathopt; /* set by padvance */
2232
2233static char *
2234padvance(const char **path, const char *name)
2235{
2236 const char *p;
2237 char *q;
2238 const char *start;
2239 size_t len;
2240
2241 if (*path == NULL)
2242 return NULL;
2243 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002244 for (p = start; *p && *p != ':' && *p != '%'; p++)
2245 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002246 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2247 while (stackblocksize() < len)
2248 growstackblock();
2249 q = stackblock();
2250 if (p != start) {
2251 memcpy(q, start, p - start);
2252 q += p - start;
2253 *q++ = '/';
2254 }
2255 strcpy(q, name);
2256 pathopt = NULL;
2257 if (*p == '%') {
2258 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002259 while (*p && *p != ':')
2260 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002261 }
2262 if (*p == ':')
2263 *path = p + 1;
2264 else
2265 *path = NULL;
2266 return stalloc(len);
2267}
2268
2269
2270/* ============ Prompt */
2271
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002272static smallint doprompt; /* if set, prompt the user */
2273static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002274
2275#if ENABLE_FEATURE_EDITING
2276static line_input_t *line_input_state;
2277static const char *cmdedit_prompt;
2278static void
2279putprompt(const char *s)
2280{
2281 if (ENABLE_ASH_EXPAND_PRMT) {
2282 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002283 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002284 return;
2285 }
2286 cmdedit_prompt = s;
2287}
2288#else
2289static void
2290putprompt(const char *s)
2291{
2292 out2str(s);
2293}
2294#endif
2295
2296#if ENABLE_ASH_EXPAND_PRMT
2297/* expandstr() needs parsing machinery, so it is far away ahead... */
2298static const char *expandstr(const char *ps);
2299#else
2300#define expandstr(s) s
2301#endif
2302
2303static void
2304setprompt(int whichprompt)
2305{
2306 const char *prompt;
2307#if ENABLE_ASH_EXPAND_PRMT
2308 struct stackmark smark;
2309#endif
2310
2311 needprompt = 0;
2312
2313 switch (whichprompt) {
2314 case 1:
2315 prompt = ps1val();
2316 break;
2317 case 2:
2318 prompt = ps2val();
2319 break;
2320 default: /* 0 */
2321 prompt = nullstr;
2322 }
2323#if ENABLE_ASH_EXPAND_PRMT
2324 setstackmark(&smark);
2325 stalloc(stackblocksize());
2326#endif
2327 putprompt(expandstr(prompt));
2328#if ENABLE_ASH_EXPAND_PRMT
2329 popstackmark(&smark);
2330#endif
2331}
2332
2333
2334/* ============ The cd and pwd commands */
2335
2336#define CD_PHYSICAL 1
2337#define CD_PRINT 2
2338
2339static int docd(const char *, int);
2340
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002341static int
2342cdopt(void)
2343{
2344 int flags = 0;
2345 int i, j;
2346
2347 j = 'L';
2348 while ((i = nextopt("LP"))) {
2349 if (i != j) {
2350 flags ^= CD_PHYSICAL;
2351 j = i;
2352 }
2353 }
2354
2355 return flags;
2356}
2357
2358/*
2359 * Update curdir (the name of the current directory) in response to a
2360 * cd command.
2361 */
2362static const char *
2363updatepwd(const char *dir)
2364{
2365 char *new;
2366 char *p;
2367 char *cdcomppath;
2368 const char *lim;
2369
2370 cdcomppath = ststrdup(dir);
2371 STARTSTACKSTR(new);
2372 if (*dir != '/') {
2373 if (curdir == nullstr)
2374 return 0;
2375 new = stack_putstr(curdir, new);
2376 }
2377 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002378 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002379 if (*dir != '/') {
2380 if (new[-1] != '/')
2381 USTPUTC('/', new);
2382 if (new > lim && *lim == '/')
2383 lim++;
2384 } else {
2385 USTPUTC('/', new);
2386 cdcomppath++;
2387 if (dir[1] == '/' && dir[2] != '/') {
2388 USTPUTC('/', new);
2389 cdcomppath++;
2390 lim++;
2391 }
2392 }
2393 p = strtok(cdcomppath, "/");
2394 while (p) {
2395 switch (*p) {
2396 case '.':
2397 if (p[1] == '.' && p[2] == '\0') {
2398 while (new > lim) {
2399 STUNPUTC(new);
2400 if (new[-1] == '/')
2401 break;
2402 }
2403 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002404 }
2405 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002406 break;
2407 /* fall through */
2408 default:
2409 new = stack_putstr(p, new);
2410 USTPUTC('/', new);
2411 }
2412 p = strtok(0, "/");
2413 }
2414 if (new > lim)
2415 STUNPUTC(new);
2416 *new = 0;
2417 return stackblock();
2418}
2419
2420/*
2421 * Find out what the current directory is. If we already know the current
2422 * directory, this routine returns immediately.
2423 */
2424static char *
2425getpwd(void)
2426{
Denis Vlasenko01631112007-12-16 17:20:38 +00002427 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002428 return dir ? dir : nullstr;
2429}
2430
2431static void
2432setpwd(const char *val, int setold)
2433{
2434 char *oldcur, *dir;
2435
2436 oldcur = dir = curdir;
2437
2438 if (setold) {
2439 setvar("OLDPWD", oldcur, VEXPORT);
2440 }
2441 INT_OFF;
2442 if (physdir != nullstr) {
2443 if (physdir != oldcur)
2444 free(physdir);
2445 physdir = nullstr;
2446 }
2447 if (oldcur == val || !val) {
2448 char *s = getpwd();
2449 physdir = s;
2450 if (!val)
2451 dir = s;
2452 } else
2453 dir = ckstrdup(val);
2454 if (oldcur != dir && oldcur != nullstr) {
2455 free(oldcur);
2456 }
2457 curdir = dir;
2458 INT_ON;
2459 setvar("PWD", dir, VEXPORT);
2460}
2461
2462static void hashcd(void);
2463
2464/*
2465 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2466 * know that the current directory has changed.
2467 */
2468static int
2469docd(const char *dest, int flags)
2470{
2471 const char *dir = 0;
2472 int err;
2473
2474 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2475
2476 INT_OFF;
2477 if (!(flags & CD_PHYSICAL)) {
2478 dir = updatepwd(dest);
2479 if (dir)
2480 dest = dir;
2481 }
2482 err = chdir(dest);
2483 if (err)
2484 goto out;
2485 setpwd(dir, 1);
2486 hashcd();
2487 out:
2488 INT_ON;
2489 return err;
2490}
2491
2492static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002493cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002494{
2495 const char *dest;
2496 const char *path;
2497 const char *p;
2498 char c;
2499 struct stat statb;
2500 int flags;
2501
2502 flags = cdopt();
2503 dest = *argptr;
2504 if (!dest)
2505 dest = bltinlookup(homestr);
2506 else if (LONE_DASH(dest)) {
2507 dest = bltinlookup("OLDPWD");
2508 flags |= CD_PRINT;
2509 }
2510 if (!dest)
2511 dest = nullstr;
2512 if (*dest == '/')
2513 goto step7;
2514 if (*dest == '.') {
2515 c = dest[1];
2516 dotdot:
2517 switch (c) {
2518 case '\0':
2519 case '/':
2520 goto step6;
2521 case '.':
2522 c = dest[2];
2523 if (c != '.')
2524 goto dotdot;
2525 }
2526 }
2527 if (!*dest)
2528 dest = ".";
2529 path = bltinlookup("CDPATH");
2530 if (!path) {
2531 step6:
2532 step7:
2533 p = dest;
2534 goto docd;
2535 }
2536 do {
2537 c = *path;
2538 p = padvance(&path, dest);
2539 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2540 if (c && c != ':')
2541 flags |= CD_PRINT;
2542 docd:
2543 if (!docd(p, flags))
2544 goto out;
2545 break;
2546 }
2547 } while (path);
2548 ash_msg_and_raise_error("can't cd to %s", dest);
2549 /* NOTREACHED */
2550 out:
2551 if (flags & CD_PRINT)
2552 out1fmt(snlfmt, curdir);
2553 return 0;
2554}
2555
2556static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002557pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002558{
2559 int flags;
2560 const char *dir = curdir;
2561
2562 flags = cdopt();
2563 if (flags) {
2564 if (physdir == nullstr)
2565 setpwd(dir, 0);
2566 dir = physdir;
2567 }
2568 out1fmt(snlfmt, dir);
2569 return 0;
2570}
2571
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002572
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002573/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002574
Denis Vlasenko834dee72008-10-07 09:18:30 +00002575
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002576#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002577/* buffer for top level input file */
2578#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002579
Eric Andersenc470f442003-07-28 09:56:35 +00002580/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002581#define CWORD 0 /* character is nothing special */
2582#define CNL 1 /* newline character */
2583#define CBACK 2 /* a backslash character */
2584#define CSQUOTE 3 /* single quote */
2585#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002586#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002587#define CBQUOTE 6 /* backwards single quote */
2588#define CVAR 7 /* a dollar sign */
2589#define CENDVAR 8 /* a '}' character */
2590#define CLP 9 /* a left paren in arithmetic */
2591#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002592#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002593#define CCTL 12 /* like CWORD, except it must be escaped */
2594#define CSPCL 13 /* these terminate a word */
2595#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002596
Denis Vlasenko131ae172007-02-18 13:00:19 +00002597#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002598#define SYNBASE 130
2599#define PEOF -130
2600#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002601#define PEOA_OR_PEOF PEOA
2602#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002603#define SYNBASE 129
2604#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002605#define PEOA_OR_PEOF PEOF
2606#endif
2607
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002608/* number syntax index */
2609#define BASESYNTAX 0 /* not in quotes */
2610#define DQSYNTAX 1 /* in double quotes */
2611#define SQSYNTAX 2 /* in single quotes */
2612#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002613#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002614
Denis Vlasenko131ae172007-02-18 13:00:19 +00002615#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002616#define USE_SIT_FUNCTION
2617#endif
2618
Denis Vlasenko131ae172007-02-18 13:00:19 +00002619#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002620static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002621#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002622 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002623#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002624 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2625 { CNL, CNL, CNL, CNL }, /* 2, \n */
2626 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2627 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2628 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2629 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2630 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2631 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2632 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2633 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2634 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002635#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002636 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2637 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2638 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002639#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002640};
Eric Andersenc470f442003-07-28 09:56:35 +00002641#else
2642static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002643#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002644 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002645#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002646 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2647 { CNL, CNL, CNL }, /* 2, \n */
2648 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2649 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2650 { CVAR, CVAR, CWORD }, /* 5, $ */
2651 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2652 { CSPCL, CWORD, CWORD }, /* 7, ( */
2653 { CSPCL, CWORD, CWORD }, /* 8, ) */
2654 { CBACK, CBACK, CCTL }, /* 9, \ */
2655 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2656 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002657#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002658 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2659 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2660 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002661#endif
2662};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002663#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002664
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002665#ifdef USE_SIT_FUNCTION
2666
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002667static int
2668SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002669{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002670 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002671#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002672 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002673 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2674 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2675 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2676 11, 3 /* "}~" */
2677 };
2678#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002679 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002680 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2681 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2682 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2683 10, 2 /* "}~" */
2684 };
2685#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002686 const char *s;
2687 int indx;
2688
Eric Andersenc470f442003-07-28 09:56:35 +00002689 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002690 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002691#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002692 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002693 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002694 else
2695#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002696
2697 if ((unsigned char)c >= (unsigned char)(CTLESC)
2698 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2699 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002700 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002701 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002702 s = strchrnul(spec_symbls, c);
2703 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002704 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002705 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002706 }
2707 return S_I_T[indx][syntax];
2708}
2709
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002710#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002711
Denis Vlasenko131ae172007-02-18 13:00:19 +00002712#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002713#define CSPCL_CIGN_CIGN_CIGN 0
2714#define CSPCL_CWORD_CWORD_CWORD 1
2715#define CNL_CNL_CNL_CNL 2
2716#define CWORD_CCTL_CCTL_CWORD 3
2717#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2718#define CVAR_CVAR_CWORD_CVAR 5
2719#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2720#define CSPCL_CWORD_CWORD_CLP 7
2721#define CSPCL_CWORD_CWORD_CRP 8
2722#define CBACK_CBACK_CCTL_CBACK 9
2723#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2724#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2725#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2726#define CWORD_CWORD_CWORD_CWORD 13
2727#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002728#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002729#define CSPCL_CWORD_CWORD_CWORD 0
2730#define CNL_CNL_CNL_CNL 1
2731#define CWORD_CCTL_CCTL_CWORD 2
2732#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2733#define CVAR_CVAR_CWORD_CVAR 4
2734#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2735#define CSPCL_CWORD_CWORD_CLP 6
2736#define CSPCL_CWORD_CWORD_CRP 7
2737#define CBACK_CBACK_CCTL_CBACK 8
2738#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2739#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2740#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2741#define CWORD_CWORD_CWORD_CWORD 12
2742#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002743#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002744
2745static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002746 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002747 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002748#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002749 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2750#endif
2751 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2753 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2754 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2755 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2756 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2757 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2758 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2759 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002760 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2889 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2890 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2912 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002913 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002914 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2916 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002918 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002919 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2920 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2921 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2922 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2924 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2925 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2926 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2927 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2938 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2939 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2940 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2942 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2943 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2971 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2972 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2973 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2976 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3004 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3005 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3006 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003007};
3008
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003009#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3010
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003011#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003012
Eric Andersen2870d962001-07-02 17:27:21 +00003013
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003014/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003015
Denis Vlasenko131ae172007-02-18 13:00:19 +00003016#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003017
3018#define ALIASINUSE 1
3019#define ALIASDEAD 2
3020
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003021struct alias {
3022 struct alias *next;
3023 char *name;
3024 char *val;
3025 int flag;
3026};
3027
Denis Vlasenko01631112007-12-16 17:20:38 +00003028
3029static struct alias **atab; // [ATABSIZE];
3030#define INIT_G_alias() do { \
3031 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3032} while (0)
3033
Eric Andersen2870d962001-07-02 17:27:21 +00003034
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003035static struct alias **
3036__lookupalias(const char *name) {
3037 unsigned int hashval;
3038 struct alias **app;
3039 const char *p;
3040 unsigned int ch;
3041
3042 p = name;
3043
3044 ch = (unsigned char)*p;
3045 hashval = ch << 4;
3046 while (ch) {
3047 hashval += ch;
3048 ch = (unsigned char)*++p;
3049 }
3050 app = &atab[hashval % ATABSIZE];
3051
3052 for (; *app; app = &(*app)->next) {
3053 if (strcmp(name, (*app)->name) == 0) {
3054 break;
3055 }
3056 }
3057
3058 return app;
3059}
3060
3061static struct alias *
3062lookupalias(const char *name, int check)
3063{
3064 struct alias *ap = *__lookupalias(name);
3065
3066 if (check && ap && (ap->flag & ALIASINUSE))
3067 return NULL;
3068 return ap;
3069}
3070
3071static struct alias *
3072freealias(struct alias *ap)
3073{
3074 struct alias *next;
3075
3076 if (ap->flag & ALIASINUSE) {
3077 ap->flag |= ALIASDEAD;
3078 return ap;
3079 }
3080
3081 next = ap->next;
3082 free(ap->name);
3083 free(ap->val);
3084 free(ap);
3085 return next;
3086}
Eric Andersencb57d552001-06-28 07:25:16 +00003087
Eric Andersenc470f442003-07-28 09:56:35 +00003088static void
3089setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003090{
3091 struct alias *ap, **app;
3092
3093 app = __lookupalias(name);
3094 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003095 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003096 if (ap) {
3097 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003098 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003099 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003100 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003101 ap->flag &= ~ALIASDEAD;
3102 } else {
3103 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003104 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003105 ap->name = ckstrdup(name);
3106 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003107 /*ap->flag = 0; - ckzalloc did it */
3108 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003109 *app = ap;
3110 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003111 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003112}
3113
Eric Andersenc470f442003-07-28 09:56:35 +00003114static int
3115unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003116{
Eric Andersencb57d552001-06-28 07:25:16 +00003117 struct alias **app;
3118
3119 app = __lookupalias(name);
3120
3121 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003122 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003123 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003124 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003125 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003126 }
3127
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003128 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003129}
3130
Eric Andersenc470f442003-07-28 09:56:35 +00003131static void
3132rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003133{
Eric Andersencb57d552001-06-28 07:25:16 +00003134 struct alias *ap, **app;
3135 int i;
3136
Denis Vlasenkob012b102007-02-19 22:43:01 +00003137 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003138 for (i = 0; i < ATABSIZE; i++) {
3139 app = &atab[i];
3140 for (ap = *app; ap; ap = *app) {
3141 *app = freealias(*app);
3142 if (ap == *app) {
3143 app = &ap->next;
3144 }
3145 }
3146 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003147 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003148}
3149
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003150static void
3151printalias(const struct alias *ap)
3152{
3153 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3154}
3155
Eric Andersencb57d552001-06-28 07:25:16 +00003156/*
3157 * TODO - sort output
3158 */
Eric Andersenc470f442003-07-28 09:56:35 +00003159static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003160aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003161{
3162 char *n, *v;
3163 int ret = 0;
3164 struct alias *ap;
3165
Denis Vlasenko68404f12008-03-17 09:00:54 +00003166 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003167 int i;
3168
Denis Vlasenko68404f12008-03-17 09:00:54 +00003169 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003170 for (ap = atab[i]; ap; ap = ap->next) {
3171 printalias(ap);
3172 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003173 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003174 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003175 }
3176 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003177 v = strchr(n+1, '=');
3178 if (v == NULL) { /* n+1: funny ksh stuff */
3179 ap = *__lookupalias(n);
3180 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003181 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003182 ret = 1;
3183 } else
3184 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003185 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003186 *v++ = '\0';
3187 setalias(n, v);
3188 }
3189 }
3190
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003191 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003192}
3193
Eric Andersenc470f442003-07-28 09:56:35 +00003194static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003195unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003196{
3197 int i;
3198
3199 while ((i = nextopt("a")) != '\0') {
3200 if (i == 'a') {
3201 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003202 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003203 }
3204 }
3205 for (i = 0; *argptr; argptr++) {
3206 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003207 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003208 i = 1;
3209 }
3210 }
3211
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003212 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003213}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003214
Denis Vlasenko131ae172007-02-18 13:00:19 +00003215#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003216
Eric Andersenc470f442003-07-28 09:56:35 +00003217
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003218/* ============ jobs.c */
3219
3220/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3221#define FORK_FG 0
3222#define FORK_BG 1
3223#define FORK_NOJOB 2
3224
3225/* mode flags for showjob(s) */
3226#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3227#define SHOW_PID 0x04 /* include process pid */
3228#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3229
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003230/*
3231 * A job structure contains information about a job. A job is either a
3232 * single process or a set of processes contained in a pipeline. In the
3233 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3234 * array of pids.
3235 */
3236
3237struct procstat {
3238 pid_t pid; /* process id */
3239 int status; /* last process status from wait() */
3240 char *cmd; /* text of command being run */
3241};
3242
3243struct job {
3244 struct procstat ps0; /* status of process */
3245 struct procstat *ps; /* status or processes when more than one */
3246#if JOBS
3247 int stopstatus; /* status of a stopped job */
3248#endif
3249 uint32_t
3250 nprocs: 16, /* number of processes */
3251 state: 8,
3252#define JOBRUNNING 0 /* at least one proc running */
3253#define JOBSTOPPED 1 /* all procs are stopped */
3254#define JOBDONE 2 /* all procs are completed */
3255#if JOBS
3256 sigint: 1, /* job was killed by SIGINT */
3257 jobctl: 1, /* job running under job control */
3258#endif
3259 waited: 1, /* true if this entry has been waited for */
3260 used: 1, /* true if this entry is in used */
3261 changed: 1; /* true if status has changed */
3262 struct job *prev_job; /* previous job */
3263};
3264
Denis Vlasenko68404f12008-03-17 09:00:54 +00003265static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003266#if !JOBS
3267#define forkshell(job, node, mode) forkshell(job, mode)
3268#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003269static int forkshell(struct job *, union node *, int);
3270static int waitforjob(struct job *);
3271
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003272#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003273enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003274#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003275#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003276static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003277static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003278#endif
3279
3280/*
3281 * Set the signal handler for the specified signal. The routine figures
3282 * out what it should be set to.
3283 */
3284static void
3285setsignal(int signo)
3286{
3287 int action;
3288 char *t, tsig;
3289 struct sigaction act;
3290
3291 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003292 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003293 if (t == NULL)
3294 action = S_DFL;
3295 else if (*t != '\0')
3296 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003297 if (rootshell && action == S_DFL) {
3298 switch (signo) {
3299 case SIGINT:
3300 if (iflag || minusc || sflag == 0)
3301 action = S_CATCH;
3302 break;
3303 case SIGQUIT:
3304#if DEBUG
3305 if (debug)
3306 break;
3307#endif
3308 /* FALLTHROUGH */
3309 case SIGTERM:
3310 if (iflag)
3311 action = S_IGN;
3312 break;
3313#if JOBS
3314 case SIGTSTP:
3315 case SIGTTOU:
3316 if (mflag)
3317 action = S_IGN;
3318 break;
3319#endif
3320 }
3321 }
3322
3323 t = &sigmode[signo - 1];
3324 tsig = *t;
3325 if (tsig == 0) {
3326 /*
3327 * current setting unknown
3328 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003329 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003330 /*
3331 * Pretend it worked; maybe we should give a warning
3332 * here, but other shells don't. We don't alter
3333 * sigmode, so that we retry every time.
3334 */
3335 return;
3336 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003337 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003339 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003340 if (mflag
3341 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3342 ) {
3343 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003344 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003345 }
3346 }
3347 if (tsig == S_HARD_IGN || tsig == action)
3348 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003349 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003350 switch (action) {
3351 case S_CATCH:
3352 act.sa_handler = onsig;
3353 break;
3354 case S_IGN:
3355 act.sa_handler = SIG_IGN;
3356 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003357 }
3358 *t = action;
3359 act.sa_flags = 0;
3360 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003361 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362}
3363
3364/* mode flags for set_curjob */
3365#define CUR_DELETE 2
3366#define CUR_RUNNING 1
3367#define CUR_STOPPED 0
3368
3369/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003370#define DOWAIT_NONBLOCK WNOHANG
3371#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003372
3373#if JOBS
3374/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003375static int initialpgrp; //references:2
3376static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003377#endif
3378/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003379static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003380/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003381static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003382/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003383static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003384/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003385static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003386
3387static void
3388set_curjob(struct job *jp, unsigned mode)
3389{
3390 struct job *jp1;
3391 struct job **jpp, **curp;
3392
3393 /* first remove from list */
3394 jpp = curp = &curjob;
3395 do {
3396 jp1 = *jpp;
3397 if (jp1 == jp)
3398 break;
3399 jpp = &jp1->prev_job;
3400 } while (1);
3401 *jpp = jp1->prev_job;
3402
3403 /* Then re-insert in correct position */
3404 jpp = curp;
3405 switch (mode) {
3406 default:
3407#if DEBUG
3408 abort();
3409#endif
3410 case CUR_DELETE:
3411 /* job being deleted */
3412 break;
3413 case CUR_RUNNING:
3414 /* newly created job or backgrounded job,
3415 put after all stopped jobs. */
3416 do {
3417 jp1 = *jpp;
3418#if JOBS
3419 if (!jp1 || jp1->state != JOBSTOPPED)
3420#endif
3421 break;
3422 jpp = &jp1->prev_job;
3423 } while (1);
3424 /* FALLTHROUGH */
3425#if JOBS
3426 case CUR_STOPPED:
3427#endif
3428 /* newly stopped job - becomes curjob */
3429 jp->prev_job = *jpp;
3430 *jpp = jp;
3431 break;
3432 }
3433}
3434
3435#if JOBS || DEBUG
3436static int
3437jobno(const struct job *jp)
3438{
3439 return jp - jobtab + 1;
3440}
3441#endif
3442
3443/*
3444 * Convert a job name to a job structure.
3445 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003446#if !JOBS
3447#define getjob(name, getctl) getjob(name)
3448#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003449static struct job *
3450getjob(const char *name, int getctl)
3451{
3452 struct job *jp;
3453 struct job *found;
3454 const char *err_msg = "No such job: %s";
3455 unsigned num;
3456 int c;
3457 const char *p;
3458 char *(*match)(const char *, const char *);
3459
3460 jp = curjob;
3461 p = name;
3462 if (!p)
3463 goto currentjob;
3464
3465 if (*p != '%')
3466 goto err;
3467
3468 c = *++p;
3469 if (!c)
3470 goto currentjob;
3471
3472 if (!p[1]) {
3473 if (c == '+' || c == '%') {
3474 currentjob:
3475 err_msg = "No current job";
3476 goto check;
3477 }
3478 if (c == '-') {
3479 if (jp)
3480 jp = jp->prev_job;
3481 err_msg = "No previous job";
3482 check:
3483 if (!jp)
3484 goto err;
3485 goto gotit;
3486 }
3487 }
3488
3489 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003490// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003491 num = atoi(p);
3492 if (num < njobs) {
3493 jp = jobtab + num - 1;
3494 if (jp->used)
3495 goto gotit;
3496 goto err;
3497 }
3498 }
3499
3500 match = prefix;
3501 if (*p == '?') {
3502 match = strstr;
3503 p++;
3504 }
3505
3506 found = 0;
3507 while (1) {
3508 if (!jp)
3509 goto err;
3510 if (match(jp->ps[0].cmd, p)) {
3511 if (found)
3512 goto err;
3513 found = jp;
3514 err_msg = "%s: ambiguous";
3515 }
3516 jp = jp->prev_job;
3517 }
3518
3519 gotit:
3520#if JOBS
3521 err_msg = "job %s not created under job control";
3522 if (getctl && jp->jobctl == 0)
3523 goto err;
3524#endif
3525 return jp;
3526 err:
3527 ash_msg_and_raise_error(err_msg, name);
3528}
3529
3530/*
3531 * Mark a job structure as unused.
3532 */
3533static void
3534freejob(struct job *jp)
3535{
3536 struct procstat *ps;
3537 int i;
3538
3539 INT_OFF;
3540 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3541 if (ps->cmd != nullstr)
3542 free(ps->cmd);
3543 }
3544 if (jp->ps != &jp->ps0)
3545 free(jp->ps);
3546 jp->used = 0;
3547 set_curjob(jp, CUR_DELETE);
3548 INT_ON;
3549}
3550
3551#if JOBS
3552static void
3553xtcsetpgrp(int fd, pid_t pgrp)
3554{
3555 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003556 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003557}
3558
3559/*
3560 * Turn job control on and off.
3561 *
3562 * Note: This code assumes that the third arg to ioctl is a character
3563 * pointer, which is true on Berkeley systems but not System V. Since
3564 * System V doesn't have job control yet, this isn't a problem now.
3565 *
3566 * Called with interrupts off.
3567 */
3568static void
3569setjobctl(int on)
3570{
3571 int fd;
3572 int pgrp;
3573
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003574 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003575 return;
3576 if (on) {
3577 int ofd;
3578 ofd = fd = open(_PATH_TTY, O_RDWR);
3579 if (fd < 0) {
3580 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3581 * That sometimes helps to acquire controlling tty.
3582 * Obviously, a workaround for bugs when someone
3583 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003584 fd = 2;
3585 while (!isatty(fd))
3586 if (--fd < 0)
3587 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003588 }
3589 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003590 if (ofd >= 0)
3591 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003592 if (fd < 0)
3593 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003594 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003595 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003596 do { /* while we are in the background */
3597 pgrp = tcgetpgrp(fd);
3598 if (pgrp < 0) {
3599 out:
3600 ash_msg("can't access tty; job control turned off");
3601 mflag = on = 0;
3602 goto close;
3603 }
3604 if (pgrp == getpgrp())
3605 break;
3606 killpg(0, SIGTTIN);
3607 } while (1);
3608 initialpgrp = pgrp;
3609
3610 setsignal(SIGTSTP);
3611 setsignal(SIGTTOU);
3612 setsignal(SIGTTIN);
3613 pgrp = rootpid;
3614 setpgid(0, pgrp);
3615 xtcsetpgrp(fd, pgrp);
3616 } else {
3617 /* turning job control off */
3618 fd = ttyfd;
3619 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003620 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003621 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003622 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003623 setpgid(0, pgrp);
3624 setsignal(SIGTSTP);
3625 setsignal(SIGTTOU);
3626 setsignal(SIGTTIN);
3627 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003628 if (fd >= 0)
3629 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003630 fd = -1;
3631 }
3632 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003633 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634}
3635
3636static int
3637killcmd(int argc, char **argv)
3638{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003639 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003640 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003641 do {
3642 if (argv[i][0] == '%') {
3643 struct job *jp = getjob(argv[i], 0);
3644 unsigned pid = jp->ps[0].pid;
3645 /* Enough space for ' -NNN<nul>' */
3646 argv[i] = alloca(sizeof(int)*3 + 3);
3647 /* kill_main has matching code to expect
3648 * leading space. Needed to not confuse
3649 * negative pids with "kill -SIGNAL_NO" syntax */
3650 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003651 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003652 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003653 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003654 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003655}
3656
3657static void
3658showpipe(struct job *jp, FILE *out)
3659{
3660 struct procstat *sp;
3661 struct procstat *spend;
3662
3663 spend = jp->ps + jp->nprocs;
3664 for (sp = jp->ps + 1; sp < spend; sp++)
3665 fprintf(out, " | %s", sp->cmd);
3666 outcslow('\n', out);
3667 flush_stdout_stderr();
3668}
3669
3670
3671static int
3672restartjob(struct job *jp, int mode)
3673{
3674 struct procstat *ps;
3675 int i;
3676 int status;
3677 pid_t pgid;
3678
3679 INT_OFF;
3680 if (jp->state == JOBDONE)
3681 goto out;
3682 jp->state = JOBRUNNING;
3683 pgid = jp->ps->pid;
3684 if (mode == FORK_FG)
3685 xtcsetpgrp(ttyfd, pgid);
3686 killpg(pgid, SIGCONT);
3687 ps = jp->ps;
3688 i = jp->nprocs;
3689 do {
3690 if (WIFSTOPPED(ps->status)) {
3691 ps->status = -1;
3692 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003693 ps++;
3694 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003695 out:
3696 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3697 INT_ON;
3698 return status;
3699}
3700
3701static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003702fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003703{
3704 struct job *jp;
3705 FILE *out;
3706 int mode;
3707 int retval;
3708
3709 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3710 nextopt(nullstr);
3711 argv = argptr;
3712 out = stdout;
3713 do {
3714 jp = getjob(*argv, 1);
3715 if (mode == FORK_BG) {
3716 set_curjob(jp, CUR_RUNNING);
3717 fprintf(out, "[%d] ", jobno(jp));
3718 }
3719 outstr(jp->ps->cmd, out);
3720 showpipe(jp, out);
3721 retval = restartjob(jp, mode);
3722 } while (*argv && *++argv);
3723 return retval;
3724}
3725#endif
3726
3727static int
3728sprint_status(char *s, int status, int sigonly)
3729{
3730 int col;
3731 int st;
3732
3733 col = 0;
3734 if (!WIFEXITED(status)) {
3735#if JOBS
3736 if (WIFSTOPPED(status))
3737 st = WSTOPSIG(status);
3738 else
3739#endif
3740 st = WTERMSIG(status);
3741 if (sigonly) {
3742 if (st == SIGINT || st == SIGPIPE)
3743 goto out;
3744#if JOBS
3745 if (WIFSTOPPED(status))
3746 goto out;
3747#endif
3748 }
3749 st &= 0x7f;
3750 col = fmtstr(s, 32, strsignal(st));
3751 if (WCOREDUMP(status)) {
3752 col += fmtstr(s + col, 16, " (core dumped)");
3753 }
3754 } else if (!sigonly) {
3755 st = WEXITSTATUS(status);
3756 if (st)
3757 col = fmtstr(s, 16, "Done(%d)", st);
3758 else
3759 col = fmtstr(s, 16, "Done");
3760 }
3761 out:
3762 return col;
3763}
3764
3765/*
3766 * Do a wait system call. If job control is compiled in, we accept
3767 * stopped processes. If block is zero, we return a value of zero
3768 * rather than blocking.
3769 *
3770 * System V doesn't have a non-blocking wait system call. It does
3771 * have a SIGCLD signal that is sent to a process when one of it's
3772 * children dies. The obvious way to use SIGCLD would be to install
3773 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3774 * was received, and have waitproc bump another counter when it got
3775 * the status of a process. Waitproc would then know that a wait
3776 * system call would not block if the two counters were different.
3777 * This approach doesn't work because if a process has children that
3778 * have not been waited for, System V will send it a SIGCLD when it
3779 * installs a signal handler for SIGCLD. What this means is that when
3780 * a child exits, the shell will be sent SIGCLD signals continuously
3781 * until is runs out of stack space, unless it does a wait call before
3782 * restoring the signal handler. The code below takes advantage of
3783 * this (mis)feature by installing a signal handler for SIGCLD and
3784 * then checking to see whether it was called. If there are any
3785 * children to be waited for, it will be.
3786 *
3787 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3788 * waits at all. In this case, the user will not be informed when
3789 * a background process until the next time she runs a real program
3790 * (as opposed to running a builtin command or just typing return),
3791 * and the jobs command may give out of date information.
3792 */
3793static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003794waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003797 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003798 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003799#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003800 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3801 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802}
3803
3804/*
3805 * Wait for a process to terminate.
3806 */
3807static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003808dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003809{
3810 int pid;
3811 int status;
3812 struct job *jp;
3813 struct job *thisjob;
3814 int state;
3815
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003816 TRACE(("dowait(%d) called\n", wait_flags));
3817 pid = waitproc(wait_flags, &status);
3818 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003819 if (pid <= 0) {
3820 /* If we were doing blocking wait and (probably) got EINTR,
3821 * check for pending sigs received while waiting.
3822 * (NB: can be moved into callers if needed) */
3823 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3824 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003825 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003826 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003827 INT_OFF;
3828 thisjob = NULL;
3829 for (jp = curjob; jp; jp = jp->prev_job) {
3830 struct procstat *sp;
3831 struct procstat *spend;
3832 if (jp->state == JOBDONE)
3833 continue;
3834 state = JOBDONE;
3835 spend = jp->ps + jp->nprocs;
3836 sp = jp->ps;
3837 do {
3838 if (sp->pid == pid) {
3839 TRACE(("Job %d: changing status of proc %d "
3840 "from 0x%x to 0x%x\n",
3841 jobno(jp), pid, sp->status, status));
3842 sp->status = status;
3843 thisjob = jp;
3844 }
3845 if (sp->status == -1)
3846 state = JOBRUNNING;
3847#if JOBS
3848 if (state == JOBRUNNING)
3849 continue;
3850 if (WIFSTOPPED(sp->status)) {
3851 jp->stopstatus = sp->status;
3852 state = JOBSTOPPED;
3853 }
3854#endif
3855 } while (++sp < spend);
3856 if (thisjob)
3857 goto gotjob;
3858 }
3859#if JOBS
3860 if (!WIFSTOPPED(status))
3861#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003862 jobless--;
3863 goto out;
3864
3865 gotjob:
3866 if (state != JOBRUNNING) {
3867 thisjob->changed = 1;
3868
3869 if (thisjob->state != state) {
3870 TRACE(("Job %d: changing state from %d to %d\n",
3871 jobno(thisjob), thisjob->state, state));
3872 thisjob->state = state;
3873#if JOBS
3874 if (state == JOBSTOPPED) {
3875 set_curjob(thisjob, CUR_STOPPED);
3876 }
3877#endif
3878 }
3879 }
3880
3881 out:
3882 INT_ON;
3883
3884 if (thisjob && thisjob == job) {
3885 char s[48 + 1];
3886 int len;
3887
3888 len = sprint_status(s, status, 1);
3889 if (len) {
3890 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003891 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003892 out2str(s);
3893 }
3894 }
3895 return pid;
3896}
3897
3898#if JOBS
3899static void
3900showjob(FILE *out, struct job *jp, int mode)
3901{
3902 struct procstat *ps;
3903 struct procstat *psend;
3904 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003905 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003906 char s[80];
3907
3908 ps = jp->ps;
3909
3910 if (mode & SHOW_PGID) {
3911 /* just output process (group) id of pipeline */
3912 fprintf(out, "%d\n", ps->pid);
3913 return;
3914 }
3915
3916 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003917 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003918
3919 if (jp == curjob)
3920 s[col - 2] = '+';
3921 else if (curjob && jp == curjob->prev_job)
3922 s[col - 2] = '-';
3923
3924 if (mode & SHOW_PID)
3925 col += fmtstr(s + col, 16, "%d ", ps->pid);
3926
3927 psend = ps + jp->nprocs;
3928
3929 if (jp->state == JOBRUNNING) {
3930 strcpy(s + col, "Running");
3931 col += sizeof("Running") - 1;
3932 } else {
3933 int status = psend[-1].status;
3934 if (jp->state == JOBSTOPPED)
3935 status = jp->stopstatus;
3936 col += sprint_status(s + col, status, 0);
3937 }
3938
3939 goto start;
3940
3941 do {
3942 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003943 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944 start:
3945 fprintf(out, "%s%*c%s",
3946 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3947 );
3948 if (!(mode & SHOW_PID)) {
3949 showpipe(jp, out);
3950 break;
3951 }
3952 if (++ps == psend) {
3953 outcslow('\n', out);
3954 break;
3955 }
3956 } while (1);
3957
3958 jp->changed = 0;
3959
3960 if (jp->state == JOBDONE) {
3961 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3962 freejob(jp);
3963 }
3964}
3965
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966/*
3967 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3968 * statuses have changed since the last call to showjobs.
3969 */
3970static void
3971showjobs(FILE *out, int mode)
3972{
3973 struct job *jp;
3974
3975 TRACE(("showjobs(%x) called\n", mode));
3976
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003977 /* If not even one job changed, there is nothing to do */
3978 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003979 continue;
3980
3981 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003982 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003983 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003984 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003985 }
3986}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003987
3988static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003989jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003990{
3991 int mode, m;
3992
3993 mode = 0;
3994 while ((m = nextopt("lp"))) {
3995 if (m == 'l')
3996 mode = SHOW_PID;
3997 else
3998 mode = SHOW_PGID;
3999 }
4000
4001 argv = argptr;
4002 if (*argv) {
4003 do
4004 showjob(stdout, getjob(*argv,0), mode);
4005 while (*++argv);
4006 } else
4007 showjobs(stdout, mode);
4008
4009 return 0;
4010}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004011#endif /* JOBS */
4012
4013static int
4014getstatus(struct job *job)
4015{
4016 int status;
4017 int retval;
4018
4019 status = job->ps[job->nprocs - 1].status;
4020 retval = WEXITSTATUS(status);
4021 if (!WIFEXITED(status)) {
4022#if JOBS
4023 retval = WSTOPSIG(status);
4024 if (!WIFSTOPPED(status))
4025#endif
4026 {
4027 /* XXX: limits number of signals */
4028 retval = WTERMSIG(status);
4029#if JOBS
4030 if (retval == SIGINT)
4031 job->sigint = 1;
4032#endif
4033 }
4034 retval += 128;
4035 }
4036 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4037 jobno(job), job->nprocs, status, retval));
4038 return retval;
4039}
4040
4041static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004042waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043{
4044 struct job *job;
4045 int retval;
4046 struct job *jp;
4047
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004048// exsig++;
4049// xbarrier();
4050 if (pendingsig)
4051 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052
4053 nextopt(nullstr);
4054 retval = 0;
4055
4056 argv = argptr;
4057 if (!*argv) {
4058 /* wait for all jobs */
4059 for (;;) {
4060 jp = curjob;
4061 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004062 if (!jp) /* no running procs */
4063 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064 if (jp->state == JOBRUNNING)
4065 break;
4066 jp->waited = 1;
4067 jp = jp->prev_job;
4068 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004069 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004070 }
4071 }
4072
4073 retval = 127;
4074 do {
4075 if (**argv != '%') {
4076 pid_t pid = number(*argv);
4077 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004078 while (1) {
4079 if (!job)
4080 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 if (job->ps[job->nprocs - 1].pid == pid)
4082 break;
4083 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004084 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004085 } else
4086 job = getjob(*argv, 0);
4087 /* loop until process terminated or stopped */
4088 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004089 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004090 job->waited = 1;
4091 retval = getstatus(job);
4092 repeat:
4093 ;
4094 } while (*++argv);
4095
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004096 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004097 return retval;
4098}
4099
4100static struct job *
4101growjobtab(void)
4102{
4103 size_t len;
4104 ptrdiff_t offset;
4105 struct job *jp, *jq;
4106
4107 len = njobs * sizeof(*jp);
4108 jq = jobtab;
4109 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4110
4111 offset = (char *)jp - (char *)jq;
4112 if (offset) {
4113 /* Relocate pointers */
4114 size_t l = len;
4115
4116 jq = (struct job *)((char *)jq + l);
4117 while (l) {
4118 l -= sizeof(*jp);
4119 jq--;
4120#define joff(p) ((struct job *)((char *)(p) + l))
4121#define jmove(p) (p) = (void *)((char *)(p) + offset)
4122 if (joff(jp)->ps == &jq->ps0)
4123 jmove(joff(jp)->ps);
4124 if (joff(jp)->prev_job)
4125 jmove(joff(jp)->prev_job);
4126 }
4127 if (curjob)
4128 jmove(curjob);
4129#undef joff
4130#undef jmove
4131 }
4132
4133 njobs += 4;
4134 jobtab = jp;
4135 jp = (struct job *)((char *)jp + len);
4136 jq = jp + 3;
4137 do {
4138 jq->used = 0;
4139 } while (--jq >= jp);
4140 return jp;
4141}
4142
4143/*
4144 * Return a new job structure.
4145 * Called with interrupts off.
4146 */
4147static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004148makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004149{
4150 int i;
4151 struct job *jp;
4152
4153 for (i = njobs, jp = jobtab; ; jp++) {
4154 if (--i < 0) {
4155 jp = growjobtab();
4156 break;
4157 }
4158 if (jp->used == 0)
4159 break;
4160 if (jp->state != JOBDONE || !jp->waited)
4161 continue;
4162#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004163 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004164 continue;
4165#endif
4166 freejob(jp);
4167 break;
4168 }
4169 memset(jp, 0, sizeof(*jp));
4170#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004171 /* jp->jobctl is a bitfield.
4172 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004173 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004174 jp->jobctl = 1;
4175#endif
4176 jp->prev_job = curjob;
4177 curjob = jp;
4178 jp->used = 1;
4179 jp->ps = &jp->ps0;
4180 if (nprocs > 1) {
4181 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4182 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004183 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184 jobno(jp)));
4185 return jp;
4186}
4187
4188#if JOBS
4189/*
4190 * Return a string identifying a command (to be printed by the
4191 * jobs command).
4192 */
4193static char *cmdnextc;
4194
4195static void
4196cmdputs(const char *s)
4197{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004198 static const char vstype[VSTYPE + 1][3] = {
4199 "", "}", "-", "+", "?", "=",
4200 "%", "%%", "#", "##"
4201 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4202 };
4203
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204 const char *p, *str;
4205 char c, cc[2] = " ";
4206 char *nextc;
4207 int subtype = 0;
4208 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004209
4210 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4211 p = s;
4212 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004213 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214 switch (c) {
4215 case CTLESC:
4216 c = *p++;
4217 break;
4218 case CTLVAR:
4219 subtype = *p++;
4220 if ((subtype & VSTYPE) == VSLENGTH)
4221 str = "${#";
4222 else
4223 str = "${";
4224 if (!(subtype & VSQUOTE) == !(quoted & 1))
4225 goto dostr;
4226 quoted ^= 1;
4227 c = '"';
4228 break;
4229 case CTLENDVAR:
4230 str = "\"}" + !(quoted & 1);
4231 quoted >>= 1;
4232 subtype = 0;
4233 goto dostr;
4234 case CTLBACKQ:
4235 str = "$(...)";
4236 goto dostr;
4237 case CTLBACKQ+CTLQUOTE:
4238 str = "\"$(...)\"";
4239 goto dostr;
4240#if ENABLE_ASH_MATH_SUPPORT
4241 case CTLARI:
4242 str = "$((";
4243 goto dostr;
4244 case CTLENDARI:
4245 str = "))";
4246 goto dostr;
4247#endif
4248 case CTLQUOTEMARK:
4249 quoted ^= 1;
4250 c = '"';
4251 break;
4252 case '=':
4253 if (subtype == 0)
4254 break;
4255 if ((subtype & VSTYPE) != VSNORMAL)
4256 quoted <<= 1;
4257 str = vstype[subtype & VSTYPE];
4258 if (subtype & VSNUL)
4259 c = ':';
4260 else
4261 goto checkstr;
4262 break;
4263 case '\'':
4264 case '\\':
4265 case '"':
4266 case '$':
4267 /* These can only happen inside quotes */
4268 cc[0] = c;
4269 str = cc;
4270 c = '\\';
4271 break;
4272 default:
4273 break;
4274 }
4275 USTPUTC(c, nextc);
4276 checkstr:
4277 if (!str)
4278 continue;
4279 dostr:
4280 while ((c = *str++)) {
4281 USTPUTC(c, nextc);
4282 }
4283 }
4284 if (quoted & 1) {
4285 USTPUTC('"', nextc);
4286 }
4287 *nextc = 0;
4288 cmdnextc = nextc;
4289}
4290
4291/* cmdtxt() and cmdlist() call each other */
4292static void cmdtxt(union node *n);
4293
4294static void
4295cmdlist(union node *np, int sep)
4296{
4297 for (; np; np = np->narg.next) {
4298 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004299 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004300 cmdtxt(np);
4301 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004302 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004303 }
4304}
4305
4306static void
4307cmdtxt(union node *n)
4308{
4309 union node *np;
4310 struct nodelist *lp;
4311 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004312
4313 if (!n)
4314 return;
4315 switch (n->type) {
4316 default:
4317#if DEBUG
4318 abort();
4319#endif
4320 case NPIPE:
4321 lp = n->npipe.cmdlist;
4322 for (;;) {
4323 cmdtxt(lp->n);
4324 lp = lp->next;
4325 if (!lp)
4326 break;
4327 cmdputs(" | ");
4328 }
4329 break;
4330 case NSEMI:
4331 p = "; ";
4332 goto binop;
4333 case NAND:
4334 p = " && ";
4335 goto binop;
4336 case NOR:
4337 p = " || ";
4338 binop:
4339 cmdtxt(n->nbinary.ch1);
4340 cmdputs(p);
4341 n = n->nbinary.ch2;
4342 goto donode;
4343 case NREDIR:
4344 case NBACKGND:
4345 n = n->nredir.n;
4346 goto donode;
4347 case NNOT:
4348 cmdputs("!");
4349 n = n->nnot.com;
4350 donode:
4351 cmdtxt(n);
4352 break;
4353 case NIF:
4354 cmdputs("if ");
4355 cmdtxt(n->nif.test);
4356 cmdputs("; then ");
4357 n = n->nif.ifpart;
4358 if (n->nif.elsepart) {
4359 cmdtxt(n);
4360 cmdputs("; else ");
4361 n = n->nif.elsepart;
4362 }
4363 p = "; fi";
4364 goto dotail;
4365 case NSUBSHELL:
4366 cmdputs("(");
4367 n = n->nredir.n;
4368 p = ")";
4369 goto dotail;
4370 case NWHILE:
4371 p = "while ";
4372 goto until;
4373 case NUNTIL:
4374 p = "until ";
4375 until:
4376 cmdputs(p);
4377 cmdtxt(n->nbinary.ch1);
4378 n = n->nbinary.ch2;
4379 p = "; done";
4380 dodo:
4381 cmdputs("; do ");
4382 dotail:
4383 cmdtxt(n);
4384 goto dotail2;
4385 case NFOR:
4386 cmdputs("for ");
4387 cmdputs(n->nfor.var);
4388 cmdputs(" in ");
4389 cmdlist(n->nfor.args, 1);
4390 n = n->nfor.body;
4391 p = "; done";
4392 goto dodo;
4393 case NDEFUN:
4394 cmdputs(n->narg.text);
4395 p = "() { ... }";
4396 goto dotail2;
4397 case NCMD:
4398 cmdlist(n->ncmd.args, 1);
4399 cmdlist(n->ncmd.redirect, 0);
4400 break;
4401 case NARG:
4402 p = n->narg.text;
4403 dotail2:
4404 cmdputs(p);
4405 break;
4406 case NHERE:
4407 case NXHERE:
4408 p = "<<...";
4409 goto dotail2;
4410 case NCASE:
4411 cmdputs("case ");
4412 cmdputs(n->ncase.expr->narg.text);
4413 cmdputs(" in ");
4414 for (np = n->ncase.cases; np; np = np->nclist.next) {
4415 cmdtxt(np->nclist.pattern);
4416 cmdputs(") ");
4417 cmdtxt(np->nclist.body);
4418 cmdputs(";; ");
4419 }
4420 p = "esac";
4421 goto dotail2;
4422 case NTO:
4423 p = ">";
4424 goto redir;
4425 case NCLOBBER:
4426 p = ">|";
4427 goto redir;
4428 case NAPPEND:
4429 p = ">>";
4430 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004431#if ENABLE_ASH_BASH_COMPAT
4432 case NTO2:
4433#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004434 case NTOFD:
4435 p = ">&";
4436 goto redir;
4437 case NFROM:
4438 p = "<";
4439 goto redir;
4440 case NFROMFD:
4441 p = "<&";
4442 goto redir;
4443 case NFROMTO:
4444 p = "<>";
4445 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004446 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004447 cmdputs(p);
4448 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004449 cmdputs(utoa(n->ndup.dupfd));
4450 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004451 }
4452 n = n->nfile.fname;
4453 goto donode;
4454 }
4455}
4456
4457static char *
4458commandtext(union node *n)
4459{
4460 char *name;
4461
4462 STARTSTACKSTR(cmdnextc);
4463 cmdtxt(n);
4464 name = stackblock();
4465 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4466 name, cmdnextc, cmdnextc));
4467 return ckstrdup(name);
4468}
4469#endif /* JOBS */
4470
4471/*
4472 * Fork off a subshell. If we are doing job control, give the subshell its
4473 * own process group. Jp is a job structure that the job is to be added to.
4474 * N is the command that will be evaluated by the child. Both jp and n may
4475 * be NULL. The mode parameter can be one of the following:
4476 * FORK_FG - Fork off a foreground process.
4477 * FORK_BG - Fork off a background process.
4478 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4479 * process group even if job control is on.
4480 *
4481 * When job control is turned off, background processes have their standard
4482 * input redirected to /dev/null (except for the second and later processes
4483 * in a pipeline).
4484 *
4485 * Called with interrupts off.
4486 */
4487/*
4488 * Clear traps on a fork.
4489 */
4490static void
4491clear_traps(void)
4492{
4493 char **tp;
4494
4495 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004496 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004497 INT_OFF;
4498 free(*tp);
4499 *tp = NULL;
4500 if (tp != &trap[0])
4501 setsignal(tp - trap);
4502 INT_ON;
4503 }
4504 }
4505}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004506
4507/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004508static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004509
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004510/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004511static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004512forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004513{
4514 int oldlvl;
4515
4516 TRACE(("Child shell %d\n", getpid()));
4517 oldlvl = shlvl;
4518 shlvl++;
4519
4520 closescript();
4521 clear_traps();
4522#if JOBS
4523 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004524 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004525 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4526 pid_t pgrp;
4527
4528 if (jp->nprocs == 0)
4529 pgrp = getpid();
4530 else
4531 pgrp = jp->ps[0].pid;
4532 /* This can fail because we are doing it in the parent also */
4533 (void)setpgid(0, pgrp);
4534 if (mode == FORK_FG)
4535 xtcsetpgrp(ttyfd, pgrp);
4536 setsignal(SIGTSTP);
4537 setsignal(SIGTTOU);
4538 } else
4539#endif
4540 if (mode == FORK_BG) {
4541 ignoresig(SIGINT);
4542 ignoresig(SIGQUIT);
4543 if (jp->nprocs == 0) {
4544 close(0);
4545 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004546 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004547 }
4548 }
4549 if (!oldlvl && iflag) {
4550 setsignal(SIGINT);
4551 setsignal(SIGQUIT);
4552 setsignal(SIGTERM);
4553 }
4554 for (jp = curjob; jp; jp = jp->prev_job)
4555 freejob(jp);
4556 jobless = 0;
4557}
4558
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004559/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004560#if !JOBS
4561#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4562#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004563static void
4564forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4565{
4566 TRACE(("In parent shell: child = %d\n", pid));
4567 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004568 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4569 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004570 jobless++;
4571 return;
4572 }
4573#if JOBS
4574 if (mode != FORK_NOJOB && jp->jobctl) {
4575 int pgrp;
4576
4577 if (jp->nprocs == 0)
4578 pgrp = pid;
4579 else
4580 pgrp = jp->ps[0].pid;
4581 /* This can fail because we are doing it in the child also */
4582 setpgid(pid, pgrp);
4583 }
4584#endif
4585 if (mode == FORK_BG) {
4586 backgndpid = pid; /* set $! */
4587 set_curjob(jp, CUR_RUNNING);
4588 }
4589 if (jp) {
4590 struct procstat *ps = &jp->ps[jp->nprocs++];
4591 ps->pid = pid;
4592 ps->status = -1;
4593 ps->cmd = nullstr;
4594#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004595 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004596 ps->cmd = commandtext(n);
4597#endif
4598 }
4599}
4600
4601static int
4602forkshell(struct job *jp, union node *n, int mode)
4603{
4604 int pid;
4605
4606 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4607 pid = fork();
4608 if (pid < 0) {
4609 TRACE(("Fork failed, errno=%d", errno));
4610 if (jp)
4611 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004612 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004613 }
4614 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004615 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004616 else
4617 forkparent(jp, n, mode, pid);
4618 return pid;
4619}
4620
4621/*
4622 * Wait for job to finish.
4623 *
4624 * Under job control we have the problem that while a child process is
4625 * running interrupts generated by the user are sent to the child but not
4626 * to the shell. This means that an infinite loop started by an inter-
4627 * active user may be hard to kill. With job control turned off, an
4628 * interactive user may place an interactive program inside a loop. If
4629 * the interactive program catches interrupts, the user doesn't want
4630 * these interrupts to also abort the loop. The approach we take here
4631 * is to have the shell ignore interrupt signals while waiting for a
4632 * foreground process to terminate, and then send itself an interrupt
4633 * signal if the child process was terminated by an interrupt signal.
4634 * Unfortunately, some programs want to do a bit of cleanup and then
4635 * exit on interrupt; unless these processes terminate themselves by
4636 * sending a signal to themselves (instead of calling exit) they will
4637 * confuse this approach.
4638 *
4639 * Called with interrupts off.
4640 */
4641static int
4642waitforjob(struct job *jp)
4643{
4644 int st;
4645
4646 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4647 while (jp->state == JOBRUNNING) {
4648 dowait(DOWAIT_BLOCK, jp);
4649 }
4650 st = getstatus(jp);
4651#if JOBS
4652 if (jp->jobctl) {
4653 xtcsetpgrp(ttyfd, rootpid);
4654 /*
4655 * This is truly gross.
4656 * If we're doing job control, then we did a TIOCSPGRP which
4657 * caused us (the shell) to no longer be in the controlling
4658 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4659 * intuit from the subprocess exit status whether a SIGINT
4660 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4661 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004662 if (jp->sigint) /* TODO: do the same with all signals */
4663 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004664 }
4665 if (jp->state == JOBDONE)
4666#endif
4667 freejob(jp);
4668 return st;
4669}
4670
4671/*
4672 * return 1 if there are stopped jobs, otherwise 0
4673 */
4674static int
4675stoppedjobs(void)
4676{
4677 struct job *jp;
4678 int retval;
4679
4680 retval = 0;
4681 if (job_warning)
4682 goto out;
4683 jp = curjob;
4684 if (jp && jp->state == JOBSTOPPED) {
4685 out2str("You have stopped jobs.\n");
4686 job_warning = 2;
4687 retval++;
4688 }
4689 out:
4690 return retval;
4691}
4692
4693
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004694/* ============ redir.c
4695 *
4696 * Code for dealing with input/output redirection.
4697 */
4698
4699#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004700#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004701
4702/*
4703 * Open a file in noclobber mode.
4704 * The code was copied from bash.
4705 */
4706static int
4707noclobberopen(const char *fname)
4708{
4709 int r, fd;
4710 struct stat finfo, finfo2;
4711
4712 /*
4713 * If the file exists and is a regular file, return an error
4714 * immediately.
4715 */
4716 r = stat(fname, &finfo);
4717 if (r == 0 && S_ISREG(finfo.st_mode)) {
4718 errno = EEXIST;
4719 return -1;
4720 }
4721
4722 /*
4723 * If the file was not present (r != 0), make sure we open it
4724 * exclusively so that if it is created before we open it, our open
4725 * will fail. Make sure that we do not truncate an existing file.
4726 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4727 * file was not a regular file, we leave O_EXCL off.
4728 */
4729 if (r != 0)
4730 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4731 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4732
4733 /* If the open failed, return the file descriptor right away. */
4734 if (fd < 0)
4735 return fd;
4736
4737 /*
4738 * OK, the open succeeded, but the file may have been changed from a
4739 * non-regular file to a regular file between the stat and the open.
4740 * We are assuming that the O_EXCL open handles the case where FILENAME
4741 * did not exist and is symlinked to an existing file between the stat
4742 * and open.
4743 */
4744
4745 /*
4746 * If we can open it and fstat the file descriptor, and neither check
4747 * revealed that it was a regular file, and the file has not been
4748 * replaced, return the file descriptor.
4749 */
4750 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4751 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4752 return fd;
4753
4754 /* The file has been replaced. badness. */
4755 close(fd);
4756 errno = EEXIST;
4757 return -1;
4758}
4759
4760/*
4761 * Handle here documents. Normally we fork off a process to write the
4762 * data to a pipe. If the document is short, we can stuff the data in
4763 * the pipe without forking.
4764 */
4765/* openhere needs this forward reference */
4766static void expandhere(union node *arg, int fd);
4767static int
4768openhere(union node *redir)
4769{
4770 int pip[2];
4771 size_t len = 0;
4772
4773 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004774 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004775 if (redir->type == NHERE) {
4776 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004777 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004778 full_write(pip[1], redir->nhere.doc->narg.text, len);
4779 goto out;
4780 }
4781 }
4782 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004783 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004784 close(pip[0]);
4785 signal(SIGINT, SIG_IGN);
4786 signal(SIGQUIT, SIG_IGN);
4787 signal(SIGHUP, SIG_IGN);
4788#ifdef SIGTSTP
4789 signal(SIGTSTP, SIG_IGN);
4790#endif
4791 signal(SIGPIPE, SIG_DFL);
4792 if (redir->type == NHERE)
4793 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004794 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004795 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004796 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004797 }
4798 out:
4799 close(pip[1]);
4800 return pip[0];
4801}
4802
4803static int
4804openredirect(union node *redir)
4805{
4806 char *fname;
4807 int f;
4808
4809 switch (redir->nfile.type) {
4810 case NFROM:
4811 fname = redir->nfile.expfname;
4812 f = open(fname, O_RDONLY);
4813 if (f < 0)
4814 goto eopen;
4815 break;
4816 case NFROMTO:
4817 fname = redir->nfile.expfname;
4818 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4819 if (f < 0)
4820 goto ecreate;
4821 break;
4822 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004823#if ENABLE_ASH_BASH_COMPAT
4824 case NTO2:
4825#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004826 /* Take care of noclobber mode. */
4827 if (Cflag) {
4828 fname = redir->nfile.expfname;
4829 f = noclobberopen(fname);
4830 if (f < 0)
4831 goto ecreate;
4832 break;
4833 }
4834 /* FALLTHROUGH */
4835 case NCLOBBER:
4836 fname = redir->nfile.expfname;
4837 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4838 if (f < 0)
4839 goto ecreate;
4840 break;
4841 case NAPPEND:
4842 fname = redir->nfile.expfname;
4843 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4844 if (f < 0)
4845 goto ecreate;
4846 break;
4847 default:
4848#if DEBUG
4849 abort();
4850#endif
4851 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004852/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004853// case NTOFD:
4854// case NFROMFD:
4855// f = -1;
4856// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004857 case NHERE:
4858 case NXHERE:
4859 f = openhere(redir);
4860 break;
4861 }
4862
4863 return f;
4864 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004865 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004866 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004867 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004868}
4869
4870/*
4871 * Copy a file descriptor to be >= to. Returns -1
4872 * if the source file descriptor is closed, EMPTY if there are no unused
4873 * file descriptors left.
4874 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004875/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4876 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004877enum {
4878 COPYFD_EXACT = (int)~(INT_MAX),
4879 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4880};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004881static int
4882copyfd(int from, int to)
4883{
4884 int newfd;
4885
Denis Vlasenko5a867312008-07-24 19:46:38 +00004886 if (to & COPYFD_EXACT) {
4887 to &= ~COPYFD_EXACT;
4888 /*if (from != to)*/
4889 newfd = dup2(from, to);
4890 } else {
4891 newfd = fcntl(from, F_DUPFD, to);
4892 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004893 if (newfd < 0) {
4894 if (errno == EMFILE)
4895 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004896 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004897 ash_msg_and_raise_error("%d: %m", from);
4898 }
4899 return newfd;
4900}
4901
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004902/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004903struct two_fd_t {
4904 int orig, copy;
4905};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004906struct redirtab {
4907 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004908 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004909 int pair_count;
4910 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004911};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004912#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004913
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004914static int need_to_remember(struct redirtab *rp, int fd)
4915{
4916 int i;
4917
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004918 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004919 return 0;
4920
4921 for (i = 0; i < rp->pair_count; i++) {
4922 if (rp->two_fd[i].orig == fd) {
4923 /* already remembered */
4924 return 0;
4925 }
4926 }
4927 return 1;
4928}
4929
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004930/* "hidden" fd is a fd used to read scripts, or a copy of such */
4931static int is_hidden_fd(struct redirtab *rp, int fd)
4932{
4933 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004934 struct parsefile *pf;
4935
4936 if (fd == -1)
4937 return 0;
4938 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004939 while (pf) {
4940 if (fd == pf->fd) {
4941 return 1;
4942 }
4943 pf = pf->prev;
4944 }
4945 if (!rp)
4946 return 0;
4947 fd |= COPYFD_RESTORE;
4948 for (i = 0; i < rp->pair_count; i++) {
4949 if (rp->two_fd[i].copy == fd) {
4950 return 1;
4951 }
4952 }
4953 return 0;
4954}
4955
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004956/*
4957 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4958 * old file descriptors are stashed away so that the redirection can be
4959 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4960 * standard output, and the standard error if it becomes a duplicate of
4961 * stdout, is saved in memory.
4962 */
4963/* flags passed to redirect */
4964#define REDIR_PUSH 01 /* save previous values of file descriptors */
4965#define REDIR_SAVEFD2 03 /* set preverrout */
4966static void
4967redirect(union node *redir, int flags)
4968{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004969 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004970 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004971 int i;
4972 int fd;
4973 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004974 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004975
Denis Vlasenko01631112007-12-16 17:20:38 +00004976 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004977 if (!redir) {
4978 return;
4979 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004980
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004981 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004982 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004983 INT_OFF;
4984 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004985 union node *tmp = redir;
4986 do {
4987 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004988#if ENABLE_ASH_BASH_COMPAT
4989 if (redir->nfile.type == NTO2)
4990 sv_pos++;
4991#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004992 tmp = tmp->nfile.next;
4993 } while (tmp);
4994 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004995 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004996 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004997 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004998 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004999 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005000 while (sv_pos > 0) {
5001 sv_pos--;
5002 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5003 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005004 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005005
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005006 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005007 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005008 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005009 int right_fd = redir->ndup.dupfd;
5010 /* redirect from/to same file descriptor? */
5011 if (right_fd == fd)
5012 continue;
5013 /* echo >&10 and 10 is a fd opened to the sh script? */
5014 if (is_hidden_fd(sv, right_fd)) {
5015 errno = EBADF; /* as if it is closed */
5016 ash_msg_and_raise_error("%d: %m", right_fd);
5017 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005018 newfd = -1;
5019 } else {
5020 newfd = openredirect(redir); /* always >= 0 */
5021 if (fd == newfd) {
5022 /* Descriptor wasn't open before redirect.
5023 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005024 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005025 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005026 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005027 continue;
5028 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005029 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005030#if ENABLE_ASH_BASH_COMPAT
5031 redirect_more:
5032#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005033 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005034 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005035 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005036/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5037 * are closed in popredir() in the child, preventing them from leaking
5038 * into child. (popredir() also cleans up the mess in case of failures)
5039 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005040 if (i == -1) {
5041 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005042 if (i != EBADF) {
5043 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005044 if (newfd >= 0)
5045 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005046 errno = i;
5047 ash_msg_and_raise_error("%d: %m", fd);
5048 /* NOTREACHED */
5049 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005050 /* EBADF: it is not open - good, remember to close it */
5051 remember_to_close:
5052 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005053 } else { /* fd is open, save its copy */
5054 /* "exec fd>&-" should not close fds
5055 * which point to script file(s).
5056 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005057 if (is_hidden_fd(sv, fd))
5058 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005059 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005060 if (fd == 2)
5061 copied_fd2 = i;
5062 sv->two_fd[sv_pos].orig = fd;
5063 sv->two_fd[sv_pos].copy = i;
5064 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005065 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005066 if (newfd < 0) {
5067 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005068 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005069 close(fd);
5070 } else {
5071 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005072 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005073 } else if (fd != newfd) { /* move newfd to fd */
5074 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005075#if ENABLE_ASH_BASH_COMPAT
5076 if (!(redir->nfile.type == NTO2 && fd == 2))
5077#endif
5078 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005079 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005080#if ENABLE_ASH_BASH_COMPAT
5081 if (redir->nfile.type == NTO2 && fd == 1) {
5082 /* We already redirected it to fd 1, now copy it to 2 */
5083 newfd = 1;
5084 fd = 2;
5085 goto redirect_more;
5086 }
5087#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005088 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005089
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005090 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005091 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5092 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005093}
5094
5095/*
5096 * Undo the effects of the last redirection.
5097 */
5098static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005099popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005100{
5101 struct redirtab *rp;
5102 int i;
5103
Denis Vlasenko01631112007-12-16 17:20:38 +00005104 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005105 return;
5106 INT_OFF;
5107 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005108 for (i = 0; i < rp->pair_count; i++) {
5109 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005110 int copy = rp->two_fd[i].copy;
5111 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005112 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005113 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005114 continue;
5115 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005116 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005117 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005118 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005119 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005120 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005121 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005122 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005123 }
5124 }
5125 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005126 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005127 free(rp);
5128 INT_ON;
5129}
5130
5131/*
5132 * Undo all redirections. Called on error or interrupt.
5133 */
5134
5135/*
5136 * Discard all saved file descriptors.
5137 */
5138static void
5139clearredir(int drop)
5140{
5141 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005142 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005143 if (!redirlist)
5144 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005145 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005146 }
5147}
5148
5149static int
5150redirectsafe(union node *redir, int flags)
5151{
5152 int err;
5153 volatile int saveint;
5154 struct jmploc *volatile savehandler = exception_handler;
5155 struct jmploc jmploc;
5156
5157 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005158 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5159 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160 if (!err) {
5161 exception_handler = &jmploc;
5162 redirect(redir, flags);
5163 }
5164 exception_handler = savehandler;
5165 if (err && exception != EXERROR)
5166 longjmp(exception_handler->loc, 1);
5167 RESTORE_INT(saveint);
5168 return err;
5169}
5170
5171
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005172/* ============ Routines to expand arguments to commands
5173 *
5174 * We have to deal with backquotes, shell variables, and file metacharacters.
5175 */
5176
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005177#if ENABLE_ASH_MATH_SUPPORT_64
5178typedef int64_t arith_t;
5179#define arith_t_type long long
5180#else
5181typedef long arith_t;
5182#define arith_t_type long
5183#endif
5184
5185#if ENABLE_ASH_MATH_SUPPORT
5186static arith_t dash_arith(const char *);
5187static arith_t arith(const char *expr, int *perrcode);
5188#endif
5189
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005190/*
5191 * expandarg flags
5192 */
5193#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5194#define EXP_TILDE 0x2 /* do normal tilde expansion */
5195#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5196#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5197#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5198#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5199#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5200#define EXP_WORD 0x80 /* expand word in parameter expansion */
5201#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5202/*
5203 * _rmescape() flags
5204 */
5205#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5206#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5207#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5208#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5209#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5210
5211/*
5212 * Structure specifying which parts of the string should be searched
5213 * for IFS characters.
5214 */
5215struct ifsregion {
5216 struct ifsregion *next; /* next region in list */
5217 int begoff; /* offset of start of region */
5218 int endoff; /* offset of end of region */
5219 int nulonly; /* search for nul bytes only */
5220};
5221
5222struct arglist {
5223 struct strlist *list;
5224 struct strlist **lastp;
5225};
5226
5227/* output of current string */
5228static char *expdest;
5229/* list of back quote expressions */
5230static struct nodelist *argbackq;
5231/* first struct in list of ifs regions */
5232static struct ifsregion ifsfirst;
5233/* last struct in list */
5234static struct ifsregion *ifslastp;
5235/* holds expanded arg list */
5236static struct arglist exparg;
5237
5238/*
5239 * Our own itoa().
5240 */
5241static int
5242cvtnum(arith_t num)
5243{
5244 int len;
5245
5246 expdest = makestrspace(32, expdest);
5247#if ENABLE_ASH_MATH_SUPPORT_64
5248 len = fmtstr(expdest, 32, "%lld", (long long) num);
5249#else
5250 len = fmtstr(expdest, 32, "%ld", num);
5251#endif
5252 STADJUST(len, expdest);
5253 return len;
5254}
5255
5256static size_t
5257esclen(const char *start, const char *p)
5258{
5259 size_t esc = 0;
5260
5261 while (p > start && *--p == CTLESC) {
5262 esc++;
5263 }
5264 return esc;
5265}
5266
5267/*
5268 * Remove any CTLESC characters from a string.
5269 */
5270static char *
5271_rmescapes(char *str, int flag)
5272{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005273 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005274
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005275 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005276 unsigned inquotes;
5277 int notescaped;
5278 int globbing;
5279
5280 p = strpbrk(str, qchars);
5281 if (!p) {
5282 return str;
5283 }
5284 q = p;
5285 r = str;
5286 if (flag & RMESCAPE_ALLOC) {
5287 size_t len = p - str;
5288 size_t fulllen = len + strlen(p) + 1;
5289
5290 if (flag & RMESCAPE_GROW) {
5291 r = makestrspace(fulllen, expdest);
5292 } else if (flag & RMESCAPE_HEAP) {
5293 r = ckmalloc(fulllen);
5294 } else {
5295 r = stalloc(fulllen);
5296 }
5297 q = r;
5298 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005299 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005300 }
5301 }
5302 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5303 globbing = flag & RMESCAPE_GLOB;
5304 notescaped = globbing;
5305 while (*p) {
5306 if (*p == CTLQUOTEMARK) {
5307 inquotes = ~inquotes;
5308 p++;
5309 notescaped = globbing;
5310 continue;
5311 }
5312 if (*p == '\\') {
5313 /* naked back slash */
5314 notescaped = 0;
5315 goto copy;
5316 }
5317 if (*p == CTLESC) {
5318 p++;
5319 if (notescaped && inquotes && *p != '/') {
5320 *q++ = '\\';
5321 }
5322 }
5323 notescaped = globbing;
5324 copy:
5325 *q++ = *p++;
5326 }
5327 *q = '\0';
5328 if (flag & RMESCAPE_GROW) {
5329 expdest = r;
5330 STADJUST(q - r + 1, expdest);
5331 }
5332 return r;
5333}
5334#define rmescapes(p) _rmescapes((p), 0)
5335
5336#define pmatch(a, b) !fnmatch((a), (b), 0)
5337
5338/*
5339 * Prepare a pattern for a expmeta (internal glob(3)) call.
5340 *
5341 * Returns an stalloced string.
5342 */
5343static char *
5344preglob(const char *pattern, int quoted, int flag)
5345{
5346 flag |= RMESCAPE_GLOB;
5347 if (quoted) {
5348 flag |= RMESCAPE_QUOTED;
5349 }
5350 return _rmescapes((char *)pattern, flag);
5351}
5352
5353/*
5354 * Put a string on the stack.
5355 */
5356static void
5357memtodest(const char *p, size_t len, int syntax, int quotes)
5358{
5359 char *q = expdest;
5360
5361 q = makestrspace(len * 2, q);
5362
5363 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005364 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005365 if (!c)
5366 continue;
5367 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5368 USTPUTC(CTLESC, q);
5369 USTPUTC(c, q);
5370 }
5371
5372 expdest = q;
5373}
5374
5375static void
5376strtodest(const char *p, int syntax, int quotes)
5377{
5378 memtodest(p, strlen(p), syntax, quotes);
5379}
5380
5381/*
5382 * Record the fact that we have to scan this region of the
5383 * string for IFS characters.
5384 */
5385static void
5386recordregion(int start, int end, int nulonly)
5387{
5388 struct ifsregion *ifsp;
5389
5390 if (ifslastp == NULL) {
5391 ifsp = &ifsfirst;
5392 } else {
5393 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005394 ifsp = ckzalloc(sizeof(*ifsp));
5395 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005396 ifslastp->next = ifsp;
5397 INT_ON;
5398 }
5399 ifslastp = ifsp;
5400 ifslastp->begoff = start;
5401 ifslastp->endoff = end;
5402 ifslastp->nulonly = nulonly;
5403}
5404
5405static void
5406removerecordregions(int endoff)
5407{
5408 if (ifslastp == NULL)
5409 return;
5410
5411 if (ifsfirst.endoff > endoff) {
5412 while (ifsfirst.next != NULL) {
5413 struct ifsregion *ifsp;
5414 INT_OFF;
5415 ifsp = ifsfirst.next->next;
5416 free(ifsfirst.next);
5417 ifsfirst.next = ifsp;
5418 INT_ON;
5419 }
5420 if (ifsfirst.begoff > endoff)
5421 ifslastp = NULL;
5422 else {
5423 ifslastp = &ifsfirst;
5424 ifsfirst.endoff = endoff;
5425 }
5426 return;
5427 }
5428
5429 ifslastp = &ifsfirst;
5430 while (ifslastp->next && ifslastp->next->begoff < endoff)
5431 ifslastp=ifslastp->next;
5432 while (ifslastp->next != NULL) {
5433 struct ifsregion *ifsp;
5434 INT_OFF;
5435 ifsp = ifslastp->next->next;
5436 free(ifslastp->next);
5437 ifslastp->next = ifsp;
5438 INT_ON;
5439 }
5440 if (ifslastp->endoff > endoff)
5441 ifslastp->endoff = endoff;
5442}
5443
5444static char *
5445exptilde(char *startp, char *p, int flag)
5446{
5447 char c;
5448 char *name;
5449 struct passwd *pw;
5450 const char *home;
5451 int quotes = flag & (EXP_FULL | EXP_CASE);
5452 int startloc;
5453
5454 name = p + 1;
5455
5456 while ((c = *++p) != '\0') {
5457 switch (c) {
5458 case CTLESC:
5459 return startp;
5460 case CTLQUOTEMARK:
5461 return startp;
5462 case ':':
5463 if (flag & EXP_VARTILDE)
5464 goto done;
5465 break;
5466 case '/':
5467 case CTLENDVAR:
5468 goto done;
5469 }
5470 }
5471 done:
5472 *p = '\0';
5473 if (*name == '\0') {
5474 home = lookupvar(homestr);
5475 } else {
5476 pw = getpwnam(name);
5477 if (pw == NULL)
5478 goto lose;
5479 home = pw->pw_dir;
5480 }
5481 if (!home || !*home)
5482 goto lose;
5483 *p = c;
5484 startloc = expdest - (char *)stackblock();
5485 strtodest(home, SQSYNTAX, quotes);
5486 recordregion(startloc, expdest - (char *)stackblock(), 0);
5487 return p;
5488 lose:
5489 *p = c;
5490 return startp;
5491}
5492
5493/*
5494 * Execute a command inside back quotes. If it's a builtin command, we
5495 * want to save its output in a block obtained from malloc. Otherwise
5496 * we fork off a subprocess and get the output of the command via a pipe.
5497 * Should be called with interrupts off.
5498 */
5499struct backcmd { /* result of evalbackcmd */
5500 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005501 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005502 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005503 struct job *jp; /* job structure for command */
5504};
5505
5506/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005507static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005508#define EV_EXIT 01 /* exit after evaluating tree */
5509static void evaltree(union node *, int);
5510
5511static void
5512evalbackcmd(union node *n, struct backcmd *result)
5513{
5514 int saveherefd;
5515
5516 result->fd = -1;
5517 result->buf = NULL;
5518 result->nleft = 0;
5519 result->jp = NULL;
5520 if (n == NULL) {
5521 goto out;
5522 }
5523
5524 saveherefd = herefd;
5525 herefd = -1;
5526
5527 {
5528 int pip[2];
5529 struct job *jp;
5530
5531 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005532 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005533 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005534 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5535 FORCE_INT_ON;
5536 close(pip[0]);
5537 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005538 /*close(1);*/
5539 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005540 close(pip[1]);
5541 }
5542 eflag = 0;
5543 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5544 /* NOTREACHED */
5545 }
5546 close(pip[1]);
5547 result->fd = pip[0];
5548 result->jp = jp;
5549 }
5550 herefd = saveherefd;
5551 out:
5552 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5553 result->fd, result->buf, result->nleft, result->jp));
5554}
5555
5556/*
5557 * Expand stuff in backwards quotes.
5558 */
5559static void
5560expbackq(union node *cmd, int quoted, int quotes)
5561{
5562 struct backcmd in;
5563 int i;
5564 char buf[128];
5565 char *p;
5566 char *dest;
5567 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005568 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569 struct stackmark smark;
5570
5571 INT_OFF;
5572 setstackmark(&smark);
5573 dest = expdest;
5574 startloc = dest - (char *)stackblock();
5575 grabstackstr(dest);
5576 evalbackcmd(cmd, &in);
5577 popstackmark(&smark);
5578
5579 p = in.buf;
5580 i = in.nleft;
5581 if (i == 0)
5582 goto read;
5583 for (;;) {
5584 memtodest(p, i, syntax, quotes);
5585 read:
5586 if (in.fd < 0)
5587 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005588 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005589 TRACE(("expbackq: read returns %d\n", i));
5590 if (i <= 0)
5591 break;
5592 p = buf;
5593 }
5594
Denis Vlasenko60818682007-09-28 22:07:23 +00005595 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596 if (in.fd >= 0) {
5597 close(in.fd);
5598 back_exitstatus = waitforjob(in.jp);
5599 }
5600 INT_ON;
5601
5602 /* Eat all trailing newlines */
5603 dest = expdest;
5604 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5605 STUNPUTC(dest);
5606 expdest = dest;
5607
5608 if (quoted == 0)
5609 recordregion(startloc, dest - (char *)stackblock(), 0);
5610 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5611 (dest - (char *)stackblock()) - startloc,
5612 (dest - (char *)stackblock()) - startloc,
5613 stackblock() + startloc));
5614}
5615
5616#if ENABLE_ASH_MATH_SUPPORT
5617/*
5618 * Expand arithmetic expression. Backup to start of expression,
5619 * evaluate, place result in (backed up) result, adjust string position.
5620 */
5621static void
5622expari(int quotes)
5623{
5624 char *p, *start;
5625 int begoff;
5626 int flag;
5627 int len;
5628
5629 /* ifsfree(); */
5630
5631 /*
5632 * This routine is slightly over-complicated for
5633 * efficiency. Next we scan backwards looking for the
5634 * start of arithmetic.
5635 */
5636 start = stackblock();
5637 p = expdest - 1;
5638 *p = '\0';
5639 p--;
5640 do {
5641 int esc;
5642
5643 while (*p != CTLARI) {
5644 p--;
5645#if DEBUG
5646 if (p < start) {
5647 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5648 }
5649#endif
5650 }
5651
5652 esc = esclen(start, p);
5653 if (!(esc % 2)) {
5654 break;
5655 }
5656
5657 p -= esc + 1;
5658 } while (1);
5659
5660 begoff = p - start;
5661
5662 removerecordregions(begoff);
5663
5664 flag = p[1];
5665
5666 expdest = p;
5667
5668 if (quotes)
5669 rmescapes(p + 2);
5670
5671 len = cvtnum(dash_arith(p + 2));
5672
5673 if (flag != '"')
5674 recordregion(begoff, begoff + len, 0);
5675}
5676#endif
5677
5678/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005679static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005680
5681/*
5682 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5683 * characters to allow for further processing. Otherwise treat
5684 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005685 *
5686 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5687 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5688 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005689 */
5690static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005691argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005692{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005693 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005694 '=',
5695 ':',
5696 CTLQUOTEMARK,
5697 CTLENDVAR,
5698 CTLESC,
5699 CTLVAR,
5700 CTLBACKQ,
5701 CTLBACKQ | CTLQUOTE,
5702#if ENABLE_ASH_MATH_SUPPORT
5703 CTLENDARI,
5704#endif
5705 0
5706 };
5707 const char *reject = spclchars;
5708 int c;
5709 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5710 int breakall = flag & EXP_WORD;
5711 int inquotes;
5712 size_t length;
5713 int startloc;
5714
5715 if (!(flag & EXP_VARTILDE)) {
5716 reject += 2;
5717 } else if (flag & EXP_VARTILDE2) {
5718 reject++;
5719 }
5720 inquotes = 0;
5721 length = 0;
5722 if (flag & EXP_TILDE) {
5723 char *q;
5724
5725 flag &= ~EXP_TILDE;
5726 tilde:
5727 q = p;
5728 if (*q == CTLESC && (flag & EXP_QWORD))
5729 q++;
5730 if (*q == '~')
5731 p = exptilde(p, q, flag);
5732 }
5733 start:
5734 startloc = expdest - (char *)stackblock();
5735 for (;;) {
5736 length += strcspn(p + length, reject);
5737 c = p[length];
5738 if (c && (!(c & 0x80)
5739#if ENABLE_ASH_MATH_SUPPORT
5740 || c == CTLENDARI
5741#endif
5742 )) {
5743 /* c == '=' || c == ':' || c == CTLENDARI */
5744 length++;
5745 }
5746 if (length > 0) {
5747 int newloc;
5748 expdest = stack_nputstr(p, length, expdest);
5749 newloc = expdest - (char *)stackblock();
5750 if (breakall && !inquotes && newloc > startloc) {
5751 recordregion(startloc, newloc, 0);
5752 }
5753 startloc = newloc;
5754 }
5755 p += length + 1;
5756 length = 0;
5757
5758 switch (c) {
5759 case '\0':
5760 goto breakloop;
5761 case '=':
5762 if (flag & EXP_VARTILDE2) {
5763 p--;
5764 continue;
5765 }
5766 flag |= EXP_VARTILDE2;
5767 reject++;
5768 /* fall through */
5769 case ':':
5770 /*
5771 * sort of a hack - expand tildes in variable
5772 * assignments (after the first '=' and after ':'s).
5773 */
5774 if (*--p == '~') {
5775 goto tilde;
5776 }
5777 continue;
5778 }
5779
5780 switch (c) {
5781 case CTLENDVAR: /* ??? */
5782 goto breakloop;
5783 case CTLQUOTEMARK:
5784 /* "$@" syntax adherence hack */
5785 if (
5786 !inquotes &&
5787 !memcmp(p, dolatstr, 4) &&
5788 (p[4] == CTLQUOTEMARK || (
5789 p[4] == CTLENDVAR &&
5790 p[5] == CTLQUOTEMARK
5791 ))
5792 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005793 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005794 goto start;
5795 }
5796 inquotes = !inquotes;
5797 addquote:
5798 if (quotes) {
5799 p--;
5800 length++;
5801 startloc++;
5802 }
5803 break;
5804 case CTLESC:
5805 startloc++;
5806 length++;
5807 goto addquote;
5808 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005809 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005810 goto start;
5811 case CTLBACKQ:
5812 c = 0;
5813 case CTLBACKQ|CTLQUOTE:
5814 expbackq(argbackq->n, c, quotes);
5815 argbackq = argbackq->next;
5816 goto start;
5817#if ENABLE_ASH_MATH_SUPPORT
5818 case CTLENDARI:
5819 p--;
5820 expari(quotes);
5821 goto start;
5822#endif
5823 }
5824 }
5825 breakloop:
5826 ;
5827}
5828
5829static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005830scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005831 int zero)
5832{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005833// This commented out code was added by James Simmons <jsimmons@infradead.org>
5834// as part of a larger change when he added support for ${var/a/b}.
5835// However, it broke # and % operators:
5836//
5837//var=ababcdcd
5838// ok bad
5839//echo ${var#ab} abcdcd abcdcd
5840//echo ${var##ab} abcdcd abcdcd
5841//echo ${var#a*b} abcdcd ababcdcd (!)
5842//echo ${var##a*b} cdcd cdcd
5843//echo ${var#?} babcdcd ababcdcd (!)
5844//echo ${var##?} babcdcd babcdcd
5845//echo ${var#*} ababcdcd babcdcd (!)
5846//echo ${var##*}
5847//echo ${var%cd} ababcd ababcd
5848//echo ${var%%cd} ababcd abab (!)
5849//echo ${var%c*d} ababcd ababcd
5850//echo ${var%%c*d} abab ababcdcd (!)
5851//echo ${var%?} ababcdc ababcdc
5852//echo ${var%%?} ababcdc ababcdcd (!)
5853//echo ${var%*} ababcdcd ababcdcd
5854//echo ${var%%*}
5855//
5856// Commenting it back out helped. Remove it completely if it really
5857// is not needed.
5858
5859 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860 char c;
5861
5862 loc = startp;
5863 loc2 = rmesc;
5864 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005865 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005867
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 c = *loc2;
5869 if (zero) {
5870 *loc2 = '\0';
5871 s = rmesc;
5872 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005873 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005874
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005875// // chop off end if its '*'
5876// full = strrchr(str, '*');
5877// if (full && full != str)
5878// match--;
5879//
5880// // If str starts with '*' replace with s.
5881// if ((*str == '*') && strlen(s) >= match) {
5882// full = xstrdup(s);
5883// strncpy(full+strlen(s)-match+1, str+1, match-1);
5884// } else
5885// full = xstrndup(str, match);
5886// match = strncmp(s, full, strlen(full));
5887// free(full);
5888//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005889 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005890 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005891 return loc;
5892 if (quotes && *loc == CTLESC)
5893 loc++;
5894 loc++;
5895 loc2++;
5896 } while (c);
5897 return 0;
5898}
5899
5900static char *
5901scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5902 int zero)
5903{
5904 int esc = 0;
5905 char *loc;
5906 char *loc2;
5907
5908 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5909 int match;
5910 char c = *loc2;
5911 const char *s = loc2;
5912 if (zero) {
5913 *loc2 = '\0';
5914 s = rmesc;
5915 }
5916 match = pmatch(str, s);
5917 *loc2 = c;
5918 if (match)
5919 return loc;
5920 loc--;
5921 if (quotes) {
5922 if (--esc < 0) {
5923 esc = esclen(startp, loc);
5924 }
5925 if (esc % 2) {
5926 esc--;
5927 loc--;
5928 }
5929 }
5930 }
5931 return 0;
5932}
5933
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005934static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005935static void
5936varunset(const char *end, const char *var, const char *umsg, int varflags)
5937{
5938 const char *msg;
5939 const char *tail;
5940
5941 tail = nullstr;
5942 msg = "parameter not set";
5943 if (umsg) {
5944 if (*end == CTLENDVAR) {
5945 if (varflags & VSNUL)
5946 tail = " or null";
5947 } else
5948 msg = umsg;
5949 }
5950 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5951}
5952
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953#if ENABLE_ASH_BASH_COMPAT
5954static char *
5955parse_sub_pattern(char *arg, int inquotes)
5956{
5957 char *idx, *repl = NULL;
5958 unsigned char c;
5959
Denis Vlasenko2659c632008-06-14 06:04:59 +00005960 idx = arg;
5961 while (1) {
5962 c = *arg;
5963 if (!c)
5964 break;
5965 if (c == '/') {
5966 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005967 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005968 repl = idx + 1;
5969 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005970 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005971 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005972 *idx++ = c;
5973 if (!inquotes && c == '\\' && arg[1] == '\\')
5974 arg++; /* skip both \\, not just first one */
5975 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005976 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005977 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005978
5979 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005980}
5981#endif /* ENABLE_ASH_BASH_COMPAT */
5982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005983static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005984subevalvar(char *p, char *str, int strloc, int subtype,
5985 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005986{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005987 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005988 char *startp;
5989 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005990 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005991 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5992 USE_ASH_BASH_COMPAT(char null = '\0';)
5993 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5994 int saveherefd = herefd;
5995 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005997 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005998
5999 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006000 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6001 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006002 STPUTC('\0', expdest);
6003 herefd = saveherefd;
6004 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006005 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006006
6007 switch (subtype) {
6008 case VSASSIGN:
6009 setvar(str, startp, 0);
6010 amount = startp - expdest;
6011 STADJUST(amount, expdest);
6012 return startp;
6013
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006014#if ENABLE_ASH_BASH_COMPAT
6015 case VSSUBSTR:
6016 loc = str = stackblock() + strloc;
6017// TODO: number() instead? It does error checking...
6018 pos = atoi(loc);
6019 len = str - startp - 1;
6020
6021 /* *loc != '\0', guaranteed by parser */
6022 if (quotes) {
6023 char *ptr;
6024
6025 /* We must adjust the length by the number of escapes we find. */
6026 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006027 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006028 len--;
6029 ptr++;
6030 }
6031 }
6032 }
6033 orig_len = len;
6034
6035 if (*loc++ == ':') {
6036// TODO: number() instead? It does error checking...
6037 len = atoi(loc);
6038 } else {
6039 len = orig_len;
6040 while (*loc && *loc != ':')
6041 loc++;
6042 if (*loc++ == ':')
6043// TODO: number() instead? It does error checking...
6044 len = atoi(loc);
6045 }
6046 if (pos >= orig_len) {
6047 pos = 0;
6048 len = 0;
6049 }
6050 if (len > (orig_len - pos))
6051 len = orig_len - pos;
6052
6053 for (str = startp; pos; str++, pos--) {
6054 if (quotes && *str == CTLESC)
6055 str++;
6056 }
6057 for (loc = startp; len; len--) {
6058 if (quotes && *str == CTLESC)
6059 *loc++ = *str++;
6060 *loc++ = *str++;
6061 }
6062 *loc = '\0';
6063 amount = loc - expdest;
6064 STADJUST(amount, expdest);
6065 return loc;
6066#endif
6067
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006068 case VSQUESTION:
6069 varunset(p, str, startp, varflags);
6070 /* NOTREACHED */
6071 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006072 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006073
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006074 /* We'll comeback here if we grow the stack while handling
6075 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6076 * stack will need rebasing, and we'll need to remove our work
6077 * areas each time
6078 */
6079 USE_ASH_BASH_COMPAT(restart:)
6080
6081 amount = expdest - ((char *)stackblock() + resetloc);
6082 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006083 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006084
6085 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006086 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006087 if (quotes) {
6088 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6089 if (rmesc != startp) {
6090 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006091 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006092 }
6093 }
6094 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006095 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006096 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006097 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006098
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006099#if ENABLE_ASH_BASH_COMPAT
6100 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6101 char *idx, *end, *restart_detect;
6102
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006103 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006104 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6105 if (!repl)
6106 repl = &null;
6107 }
6108
6109 /* If there's no pattern to match, return the expansion unmolested */
6110 if (*str == '\0')
6111 return 0;
6112
6113 len = 0;
6114 idx = startp;
6115 end = str - 1;
6116 while (idx < end) {
6117 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6118 if (!loc) {
6119 /* No match, advance */
6120 restart_detect = stackblock();
6121 STPUTC(*idx, expdest);
6122 if (quotes && *idx == CTLESC) {
6123 idx++;
6124 len++;
6125 STPUTC(*idx, expdest);
6126 }
6127 if (stackblock() != restart_detect)
6128 goto restart;
6129 idx++;
6130 len++;
6131 rmesc++;
6132 continue;
6133 }
6134
6135 if (subtype == VSREPLACEALL) {
6136 while (idx < loc) {
6137 if (quotes && *idx == CTLESC)
6138 idx++;
6139 idx++;
6140 rmesc++;
6141 }
6142 } else
6143 idx = loc;
6144
6145 for (loc = repl; *loc; loc++) {
6146 restart_detect = stackblock();
6147 STPUTC(*loc, expdest);
6148 if (stackblock() != restart_detect)
6149 goto restart;
6150 len++;
6151 }
6152
6153 if (subtype == VSREPLACE) {
6154 while (*idx) {
6155 restart_detect = stackblock();
6156 STPUTC(*idx, expdest);
6157 if (stackblock() != restart_detect)
6158 goto restart;
6159 len++;
6160 idx++;
6161 }
6162 break;
6163 }
6164 }
6165
6166 /* We've put the replaced text into a buffer at workloc, now
6167 * move it to the right place and adjust the stack.
6168 */
6169 startp = stackblock() + startloc;
6170 STPUTC('\0', expdest);
6171 memmove(startp, stackblock() + workloc, len);
6172 startp[len++] = '\0';
6173 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6174 STADJUST(-amount, expdest);
6175 return startp;
6176 }
6177#endif /* ENABLE_ASH_BASH_COMPAT */
6178
6179 subtype -= VSTRIMRIGHT;
6180#if DEBUG
6181 if (subtype < 0 || subtype > 7)
6182 abort();
6183#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006184 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6185 zero = subtype >> 1;
6186 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6187 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6188
6189 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6190 if (loc) {
6191 if (zero) {
6192 memmove(startp, loc, str - loc);
6193 loc = startp + (str - loc) - 1;
6194 }
6195 *loc = '\0';
6196 amount = loc - expdest;
6197 STADJUST(amount, expdest);
6198 }
6199 return loc;
6200}
6201
6202/*
6203 * Add the value of a specialized variable to the stack string.
6204 */
6205static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006206varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006207{
6208 int num;
6209 char *p;
6210 int i;
6211 int sep = 0;
6212 int sepq = 0;
6213 ssize_t len = 0;
6214 char **ap;
6215 int syntax;
6216 int quoted = varflags & VSQUOTE;
6217 int subtype = varflags & VSTYPE;
6218 int quotes = flags & (EXP_FULL | EXP_CASE);
6219
6220 if (quoted && (flags & EXP_FULL))
6221 sep = 1 << CHAR_BIT;
6222
6223 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6224 switch (*name) {
6225 case '$':
6226 num = rootpid;
6227 goto numvar;
6228 case '?':
6229 num = exitstatus;
6230 goto numvar;
6231 case '#':
6232 num = shellparam.nparam;
6233 goto numvar;
6234 case '!':
6235 num = backgndpid;
6236 if (num == 0)
6237 return -1;
6238 numvar:
6239 len = cvtnum(num);
6240 break;
6241 case '-':
6242 p = makestrspace(NOPTS, expdest);
6243 for (i = NOPTS - 1; i >= 0; i--) {
6244 if (optlist[i]) {
6245 USTPUTC(optletters(i), p);
6246 len++;
6247 }
6248 }
6249 expdest = p;
6250 break;
6251 case '@':
6252 if (sep)
6253 goto param;
6254 /* fall through */
6255 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006256 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006257 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6258 sepq = 1;
6259 param:
6260 ap = shellparam.p;
6261 if (!ap)
6262 return -1;
6263 while ((p = *ap++)) {
6264 size_t partlen;
6265
6266 partlen = strlen(p);
6267 len += partlen;
6268
6269 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6270 memtodest(p, partlen, syntax, quotes);
6271
6272 if (*ap && sep) {
6273 char *q;
6274
6275 len++;
6276 if (subtype == VSPLUS || subtype == VSLENGTH) {
6277 continue;
6278 }
6279 q = expdest;
6280 if (sepq)
6281 STPUTC(CTLESC, q);
6282 STPUTC(sep, q);
6283 expdest = q;
6284 }
6285 }
6286 return len;
6287 case '0':
6288 case '1':
6289 case '2':
6290 case '3':
6291 case '4':
6292 case '5':
6293 case '6':
6294 case '7':
6295 case '8':
6296 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006297// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006298 num = atoi(name);
6299 if (num < 0 || num > shellparam.nparam)
6300 return -1;
6301 p = num ? shellparam.p[num - 1] : arg0;
6302 goto value;
6303 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006304 /* NB: name has form "VAR=..." */
6305
6306 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6307 * which should be considered before we check variables. */
6308 if (var_str_list) {
6309 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6310 p = NULL;
6311 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006312 char *str, *eq;
6313 str = var_str_list->text;
6314 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006315 if (!eq) /* stop at first non-assignment */
6316 break;
6317 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006318 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006319 && strncmp(str, name, name_len) == 0) {
6320 p = eq;
6321 /* goto value; - WRONG! */
6322 /* think "A=1 A=2 B=$A" */
6323 }
6324 var_str_list = var_str_list->next;
6325 } while (var_str_list);
6326 if (p)
6327 goto value;
6328 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329 p = lookupvar(name);
6330 value:
6331 if (!p)
6332 return -1;
6333
6334 len = strlen(p);
6335 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6336 memtodest(p, len, syntax, quotes);
6337 return len;
6338 }
6339
6340 if (subtype == VSPLUS || subtype == VSLENGTH)
6341 STADJUST(-len, expdest);
6342 return len;
6343}
6344
6345/*
6346 * Expand a variable, and return a pointer to the next character in the
6347 * input string.
6348 */
6349static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006350evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006351{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006352 char varflags;
6353 char subtype;
6354 char quoted;
6355 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356 char *var;
6357 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006358 int startloc;
6359 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006360
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006361 varflags = *p++;
6362 subtype = varflags & VSTYPE;
6363 quoted = varflags & VSQUOTE;
6364 var = p;
6365 easy = (!quoted || (*var == '@' && shellparam.nparam));
6366 startloc = expdest - (char *)stackblock();
6367 p = strchr(p, '=') + 1;
6368
6369 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006370 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006371 if (varflags & VSNUL)
6372 varlen--;
6373
6374 if (subtype == VSPLUS) {
6375 varlen = -1 - varlen;
6376 goto vsplus;
6377 }
6378
6379 if (subtype == VSMINUS) {
6380 vsplus:
6381 if (varlen < 0) {
6382 argstr(
6383 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006384 (quoted ? EXP_QWORD : EXP_WORD),
6385 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006386 );
6387 goto end;
6388 }
6389 if (easy)
6390 goto record;
6391 goto end;
6392 }
6393
6394 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6395 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006396 if (subevalvar(p, var, /* strloc: */ 0,
6397 subtype, startloc, varflags,
6398 /* quotes: */ 0,
6399 var_str_list)
6400 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006401 varflags &= ~VSNUL;
6402 /*
6403 * Remove any recorded regions beyond
6404 * start of variable
6405 */
6406 removerecordregions(startloc);
6407 goto again;
6408 }
6409 goto end;
6410 }
6411 if (easy)
6412 goto record;
6413 goto end;
6414 }
6415
6416 if (varlen < 0 && uflag)
6417 varunset(p, var, 0, 0);
6418
6419 if (subtype == VSLENGTH) {
6420 cvtnum(varlen > 0 ? varlen : 0);
6421 goto record;
6422 }
6423
6424 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006425 if (easy)
6426 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006427 goto end;
6428 }
6429
6430#if DEBUG
6431 switch (subtype) {
6432 case VSTRIMLEFT:
6433 case VSTRIMLEFTMAX:
6434 case VSTRIMRIGHT:
6435 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006436#if ENABLE_ASH_BASH_COMPAT
6437 case VSSUBSTR:
6438 case VSREPLACE:
6439 case VSREPLACEALL:
6440#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 break;
6442 default:
6443 abort();
6444 }
6445#endif
6446
6447 if (varlen >= 0) {
6448 /*
6449 * Terminate the string and start recording the pattern
6450 * right after it
6451 */
6452 STPUTC('\0', expdest);
6453 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006454 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6455 startloc, varflags,
6456 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6457 var_str_list)
6458 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006459 int amount = expdest - (
6460 (char *)stackblock() + patloc - 1
6461 );
6462 STADJUST(-amount, expdest);
6463 }
6464 /* Remove any recorded regions beyond start of variable */
6465 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006466 record:
6467 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006468 }
6469
6470 end:
6471 if (subtype != VSNORMAL) { /* skip to end of alternative */
6472 int nesting = 1;
6473 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006474 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006475 if (c == CTLESC)
6476 p++;
6477 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6478 if (varlen >= 0)
6479 argbackq = argbackq->next;
6480 } else if (c == CTLVAR) {
6481 if ((*p++ & VSTYPE) != VSNORMAL)
6482 nesting++;
6483 } else if (c == CTLENDVAR) {
6484 if (--nesting == 0)
6485 break;
6486 }
6487 }
6488 }
6489 return p;
6490}
6491
6492/*
6493 * Break the argument string into pieces based upon IFS and add the
6494 * strings to the argument list. The regions of the string to be
6495 * searched for IFS characters have been stored by recordregion.
6496 */
6497static void
6498ifsbreakup(char *string, struct arglist *arglist)
6499{
6500 struct ifsregion *ifsp;
6501 struct strlist *sp;
6502 char *start;
6503 char *p;
6504 char *q;
6505 const char *ifs, *realifs;
6506 int ifsspc;
6507 int nulonly;
6508
6509 start = string;
6510 if (ifslastp != NULL) {
6511 ifsspc = 0;
6512 nulonly = 0;
6513 realifs = ifsset() ? ifsval() : defifs;
6514 ifsp = &ifsfirst;
6515 do {
6516 p = string + ifsp->begoff;
6517 nulonly = ifsp->nulonly;
6518 ifs = nulonly ? nullstr : realifs;
6519 ifsspc = 0;
6520 while (p < string + ifsp->endoff) {
6521 q = p;
6522 if (*p == CTLESC)
6523 p++;
6524 if (!strchr(ifs, *p)) {
6525 p++;
6526 continue;
6527 }
6528 if (!nulonly)
6529 ifsspc = (strchr(defifs, *p) != NULL);
6530 /* Ignore IFS whitespace at start */
6531 if (q == start && ifsspc) {
6532 p++;
6533 start = p;
6534 continue;
6535 }
6536 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006537 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006538 sp->text = start;
6539 *arglist->lastp = sp;
6540 arglist->lastp = &sp->next;
6541 p++;
6542 if (!nulonly) {
6543 for (;;) {
6544 if (p >= string + ifsp->endoff) {
6545 break;
6546 }
6547 q = p;
6548 if (*p == CTLESC)
6549 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006550 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006551 p = q;
6552 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006553 }
6554 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006555 if (ifsspc) {
6556 p++;
6557 ifsspc = 0;
6558 } else {
6559 p = q;
6560 break;
6561 }
6562 } else
6563 p++;
6564 }
6565 }
6566 start = p;
6567 } /* while */
6568 ifsp = ifsp->next;
6569 } while (ifsp != NULL);
6570 if (nulonly)
6571 goto add;
6572 }
6573
6574 if (!*start)
6575 return;
6576
6577 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006578 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006579 sp->text = start;
6580 *arglist->lastp = sp;
6581 arglist->lastp = &sp->next;
6582}
6583
6584static void
6585ifsfree(void)
6586{
6587 struct ifsregion *p;
6588
6589 INT_OFF;
6590 p = ifsfirst.next;
6591 do {
6592 struct ifsregion *ifsp;
6593 ifsp = p->next;
6594 free(p);
6595 p = ifsp;
6596 } while (p);
6597 ifslastp = NULL;
6598 ifsfirst.next = NULL;
6599 INT_ON;
6600}
6601
6602/*
6603 * Add a file name to the list.
6604 */
6605static void
6606addfname(const char *name)
6607{
6608 struct strlist *sp;
6609
Denis Vlasenko597906c2008-02-20 16:38:54 +00006610 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006611 sp->text = ststrdup(name);
6612 *exparg.lastp = sp;
6613 exparg.lastp = &sp->next;
6614}
6615
6616static char *expdir;
6617
6618/*
6619 * Do metacharacter (i.e. *, ?, [...]) expansion.
6620 */
6621static void
6622expmeta(char *enddir, char *name)
6623{
6624 char *p;
6625 const char *cp;
6626 char *start;
6627 char *endname;
6628 int metaflag;
6629 struct stat statb;
6630 DIR *dirp;
6631 struct dirent *dp;
6632 int atend;
6633 int matchdot;
6634
6635 metaflag = 0;
6636 start = name;
6637 for (p = name; *p; p++) {
6638 if (*p == '*' || *p == '?')
6639 metaflag = 1;
6640 else if (*p == '[') {
6641 char *q = p + 1;
6642 if (*q == '!')
6643 q++;
6644 for (;;) {
6645 if (*q == '\\')
6646 q++;
6647 if (*q == '/' || *q == '\0')
6648 break;
6649 if (*++q == ']') {
6650 metaflag = 1;
6651 break;
6652 }
6653 }
6654 } else if (*p == '\\')
6655 p++;
6656 else if (*p == '/') {
6657 if (metaflag)
6658 goto out;
6659 start = p + 1;
6660 }
6661 }
6662 out:
6663 if (metaflag == 0) { /* we've reached the end of the file name */
6664 if (enddir != expdir)
6665 metaflag++;
6666 p = name;
6667 do {
6668 if (*p == '\\')
6669 p++;
6670 *enddir++ = *p;
6671 } while (*p++);
6672 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6673 addfname(expdir);
6674 return;
6675 }
6676 endname = p;
6677 if (name < start) {
6678 p = name;
6679 do {
6680 if (*p == '\\')
6681 p++;
6682 *enddir++ = *p++;
6683 } while (p < start);
6684 }
6685 if (enddir == expdir) {
6686 cp = ".";
6687 } else if (enddir == expdir + 1 && *expdir == '/') {
6688 cp = "/";
6689 } else {
6690 cp = expdir;
6691 enddir[-1] = '\0';
6692 }
6693 dirp = opendir(cp);
6694 if (dirp == NULL)
6695 return;
6696 if (enddir != expdir)
6697 enddir[-1] = '/';
6698 if (*endname == 0) {
6699 atend = 1;
6700 } else {
6701 atend = 0;
6702 *endname++ = '\0';
6703 }
6704 matchdot = 0;
6705 p = start;
6706 if (*p == '\\')
6707 p++;
6708 if (*p == '.')
6709 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006710 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006711 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006712 continue;
6713 if (pmatch(start, dp->d_name)) {
6714 if (atend) {
6715 strcpy(enddir, dp->d_name);
6716 addfname(expdir);
6717 } else {
6718 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6719 continue;
6720 p[-1] = '/';
6721 expmeta(p, endname);
6722 }
6723 }
6724 }
6725 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006726 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006727 endname[-1] = '/';
6728}
6729
6730static struct strlist *
6731msort(struct strlist *list, int len)
6732{
6733 struct strlist *p, *q = NULL;
6734 struct strlist **lpp;
6735 int half;
6736 int n;
6737
6738 if (len <= 1)
6739 return list;
6740 half = len >> 1;
6741 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006742 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006743 q = p;
6744 p = p->next;
6745 }
6746 q->next = NULL; /* terminate first half of list */
6747 q = msort(list, half); /* sort first half of list */
6748 p = msort(p, len - half); /* sort second half */
6749 lpp = &list;
6750 for (;;) {
6751#if ENABLE_LOCALE_SUPPORT
6752 if (strcoll(p->text, q->text) < 0)
6753#else
6754 if (strcmp(p->text, q->text) < 0)
6755#endif
6756 {
6757 *lpp = p;
6758 lpp = &p->next;
6759 p = *lpp;
6760 if (p == NULL) {
6761 *lpp = q;
6762 break;
6763 }
6764 } else {
6765 *lpp = q;
6766 lpp = &q->next;
6767 q = *lpp;
6768 if (q == NULL) {
6769 *lpp = p;
6770 break;
6771 }
6772 }
6773 }
6774 return list;
6775}
6776
6777/*
6778 * Sort the results of file name expansion. It calculates the number of
6779 * strings to sort and then calls msort (short for merge sort) to do the
6780 * work.
6781 */
6782static struct strlist *
6783expsort(struct strlist *str)
6784{
6785 int len;
6786 struct strlist *sp;
6787
6788 len = 0;
6789 for (sp = str; sp; sp = sp->next)
6790 len++;
6791 return msort(str, len);
6792}
6793
6794static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006795expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006796{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006797 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006798 '*', '?', '[', 0
6799 };
6800 /* TODO - EXP_REDIR */
6801
6802 while (str) {
6803 struct strlist **savelastp;
6804 struct strlist *sp;
6805 char *p;
6806
6807 if (fflag)
6808 goto nometa;
6809 if (!strpbrk(str->text, metachars))
6810 goto nometa;
6811 savelastp = exparg.lastp;
6812
6813 INT_OFF;
6814 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6815 {
6816 int i = strlen(str->text);
6817 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6818 }
6819
6820 expmeta(expdir, p);
6821 free(expdir);
6822 if (p != str->text)
6823 free(p);
6824 INT_ON;
6825 if (exparg.lastp == savelastp) {
6826 /*
6827 * no matches
6828 */
6829 nometa:
6830 *exparg.lastp = str;
6831 rmescapes(str->text);
6832 exparg.lastp = &str->next;
6833 } else {
6834 *exparg.lastp = NULL;
6835 *savelastp = sp = expsort(*savelastp);
6836 while (sp->next != NULL)
6837 sp = sp->next;
6838 exparg.lastp = &sp->next;
6839 }
6840 str = str->next;
6841 }
6842}
6843
6844/*
6845 * Perform variable substitution and command substitution on an argument,
6846 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6847 * perform splitting and file name expansion. When arglist is NULL, perform
6848 * here document expansion.
6849 */
6850static void
6851expandarg(union node *arg, struct arglist *arglist, int flag)
6852{
6853 struct strlist *sp;
6854 char *p;
6855
6856 argbackq = arg->narg.backquote;
6857 STARTSTACKSTR(expdest);
6858 ifsfirst.next = NULL;
6859 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006860 argstr(arg->narg.text, flag,
6861 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006862 p = _STPUTC('\0', expdest);
6863 expdest = p - 1;
6864 if (arglist == NULL) {
6865 return; /* here document expanded */
6866 }
6867 p = grabstackstr(p);
6868 exparg.lastp = &exparg.list;
6869 /*
6870 * TODO - EXP_REDIR
6871 */
6872 if (flag & EXP_FULL) {
6873 ifsbreakup(p, &exparg);
6874 *exparg.lastp = NULL;
6875 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006876 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006877 } else {
6878 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6879 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006880 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006881 sp->text = p;
6882 *exparg.lastp = sp;
6883 exparg.lastp = &sp->next;
6884 }
6885 if (ifsfirst.next)
6886 ifsfree();
6887 *exparg.lastp = NULL;
6888 if (exparg.list) {
6889 *arglist->lastp = exparg.list;
6890 arglist->lastp = exparg.lastp;
6891 }
6892}
6893
6894/*
6895 * Expand shell variables and backquotes inside a here document.
6896 */
6897static void
6898expandhere(union node *arg, int fd)
6899{
6900 herefd = fd;
6901 expandarg(arg, (struct arglist *)NULL, 0);
6902 full_write(fd, stackblock(), expdest - (char *)stackblock());
6903}
6904
6905/*
6906 * Returns true if the pattern matches the string.
6907 */
6908static int
6909patmatch(char *pattern, const char *string)
6910{
6911 return pmatch(preglob(pattern, 0, 0), string);
6912}
6913
6914/*
6915 * See if a pattern matches in a case statement.
6916 */
6917static int
6918casematch(union node *pattern, char *val)
6919{
6920 struct stackmark smark;
6921 int result;
6922
6923 setstackmark(&smark);
6924 argbackq = pattern->narg.backquote;
6925 STARTSTACKSTR(expdest);
6926 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006927 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6928 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006929 STACKSTRNUL(expdest);
6930 result = patmatch(stackblock(), val);
6931 popstackmark(&smark);
6932 return result;
6933}
6934
6935
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006936/* ============ find_command */
6937
6938struct builtincmd {
6939 const char *name;
6940 int (*builtin)(int, char **);
6941 /* unsigned flags; */
6942};
6943#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006944/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006945 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006946#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006947#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006948
6949struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006950 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006951 union param {
6952 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006953 /* index >= 0 for commands without path (slashes) */
6954 /* (TODO: what exactly does the value mean? PATH position?) */
6955 /* index == -1 for commands with slashes */
6956 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006957 const struct builtincmd *cmd;
6958 struct funcnode *func;
6959 } u;
6960};
6961/* values of cmdtype */
6962#define CMDUNKNOWN -1 /* no entry in table for command */
6963#define CMDNORMAL 0 /* command is an executable program */
6964#define CMDFUNCTION 1 /* command is a shell function */
6965#define CMDBUILTIN 2 /* command is a shell builtin */
6966
6967/* action to find_command() */
6968#define DO_ERR 0x01 /* prints errors */
6969#define DO_ABS 0x02 /* checks absolute paths */
6970#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6971#define DO_ALTPATH 0x08 /* using alternate path */
6972#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6973
6974static void find_command(char *, struct cmdentry *, int, const char *);
6975
6976
6977/* ============ Hashing commands */
6978
6979/*
6980 * When commands are first encountered, they are entered in a hash table.
6981 * This ensures that a full path search will not have to be done for them
6982 * on each invocation.
6983 *
6984 * We should investigate converting to a linear search, even though that
6985 * would make the command name "hash" a misnomer.
6986 */
6987
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006988struct tblentry {
6989 struct tblentry *next; /* next entry in hash chain */
6990 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006991 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006992 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006993 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006994};
6995
Denis Vlasenko01631112007-12-16 17:20:38 +00006996static struct tblentry **cmdtable;
6997#define INIT_G_cmdtable() do { \
6998 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6999} while (0)
7000
7001static int builtinloc = -1; /* index in path of %builtin, or -1 */
7002
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007003
7004static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007005tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007006{
7007 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007008
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007009#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007010 if (applet_no >= 0) {
7011 if (APPLET_IS_NOEXEC(applet_no))
7012 run_applet_no_and_exit(applet_no, argv);
7013 /* re-exec ourselves with the new arguments */
7014 execve(bb_busybox_exec_path, argv, envp);
7015 /* If they called chroot or otherwise made the binary no longer
7016 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007017 }
7018#endif
7019
7020 repeat:
7021#ifdef SYSV
7022 do {
7023 execve(cmd, argv, envp);
7024 } while (errno == EINTR);
7025#else
7026 execve(cmd, argv, envp);
7027#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007028 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007029 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007030 return;
7031 }
7032 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007033 char **ap;
7034 char **new;
7035
7036 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007037 continue;
7038 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007039 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007040 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007041 ap += 2;
7042 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007043 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007044 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007045 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007046 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007047 goto repeat;
7048 }
7049}
7050
7051/*
7052 * Exec a program. Never returns. If you change this routine, you may
7053 * have to change the find_command routine as well.
7054 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007055static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007056static void
7057shellexec(char **argv, const char *path, int idx)
7058{
7059 char *cmdname;
7060 int e;
7061 char **envp;
7062 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007063#if ENABLE_FEATURE_SH_STANDALONE
7064 int applet_no = -1;
7065#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007066
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007067 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007068 envp = listvars(VEXPORT, VUNSET, 0);
7069 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007070#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007071 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007072#endif
7073 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007074 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007075 e = errno;
7076 } else {
7077 e = ENOENT;
7078 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7079 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007080 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007081 if (errno != ENOENT && errno != ENOTDIR)
7082 e = errno;
7083 }
7084 stunalloc(cmdname);
7085 }
7086 }
7087
7088 /* Map to POSIX errors */
7089 switch (e) {
7090 case EACCES:
7091 exerrno = 126;
7092 break;
7093 case ENOENT:
7094 exerrno = 127;
7095 break;
7096 default:
7097 exerrno = 2;
7098 break;
7099 }
7100 exitstatus = exerrno;
7101 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007102 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007103 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7104 /* NOTREACHED */
7105}
7106
7107static void
7108printentry(struct tblentry *cmdp)
7109{
7110 int idx;
7111 const char *path;
7112 char *name;
7113
7114 idx = cmdp->param.index;
7115 path = pathval();
7116 do {
7117 name = padvance(&path, cmdp->cmdname);
7118 stunalloc(name);
7119 } while (--idx >= 0);
7120 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7121}
7122
7123/*
7124 * Clear out command entries. The argument specifies the first entry in
7125 * PATH which has changed.
7126 */
7127static void
7128clearcmdentry(int firstchange)
7129{
7130 struct tblentry **tblp;
7131 struct tblentry **pp;
7132 struct tblentry *cmdp;
7133
7134 INT_OFF;
7135 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7136 pp = tblp;
7137 while ((cmdp = *pp) != NULL) {
7138 if ((cmdp->cmdtype == CMDNORMAL &&
7139 cmdp->param.index >= firstchange)
7140 || (cmdp->cmdtype == CMDBUILTIN &&
7141 builtinloc >= firstchange)
7142 ) {
7143 *pp = cmdp->next;
7144 free(cmdp);
7145 } else {
7146 pp = &cmdp->next;
7147 }
7148 }
7149 }
7150 INT_ON;
7151}
7152
7153/*
7154 * Locate a command in the command hash table. If "add" is nonzero,
7155 * add the command to the table if it is not already present. The
7156 * variable "lastcmdentry" is set to point to the address of the link
7157 * pointing to the entry, so that delete_cmd_entry can delete the
7158 * entry.
7159 *
7160 * Interrupts must be off if called with add != 0.
7161 */
7162static struct tblentry **lastcmdentry;
7163
7164static struct tblentry *
7165cmdlookup(const char *name, int add)
7166{
7167 unsigned int hashval;
7168 const char *p;
7169 struct tblentry *cmdp;
7170 struct tblentry **pp;
7171
7172 p = name;
7173 hashval = (unsigned char)*p << 4;
7174 while (*p)
7175 hashval += (unsigned char)*p++;
7176 hashval &= 0x7FFF;
7177 pp = &cmdtable[hashval % CMDTABLESIZE];
7178 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7179 if (strcmp(cmdp->cmdname, name) == 0)
7180 break;
7181 pp = &cmdp->next;
7182 }
7183 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007184 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7185 + strlen(name)
7186 /* + 1 - already done because
7187 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007188 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007189 cmdp->cmdtype = CMDUNKNOWN;
7190 strcpy(cmdp->cmdname, name);
7191 }
7192 lastcmdentry = pp;
7193 return cmdp;
7194}
7195
7196/*
7197 * Delete the command entry returned on the last lookup.
7198 */
7199static void
7200delete_cmd_entry(void)
7201{
7202 struct tblentry *cmdp;
7203
7204 INT_OFF;
7205 cmdp = *lastcmdentry;
7206 *lastcmdentry = cmdp->next;
7207 if (cmdp->cmdtype == CMDFUNCTION)
7208 freefunc(cmdp->param.func);
7209 free(cmdp);
7210 INT_ON;
7211}
7212
7213/*
7214 * Add a new command entry, replacing any existing command entry for
7215 * the same name - except special builtins.
7216 */
7217static void
7218addcmdentry(char *name, struct cmdentry *entry)
7219{
7220 struct tblentry *cmdp;
7221
7222 cmdp = cmdlookup(name, 1);
7223 if (cmdp->cmdtype == CMDFUNCTION) {
7224 freefunc(cmdp->param.func);
7225 }
7226 cmdp->cmdtype = entry->cmdtype;
7227 cmdp->param = entry->u;
7228 cmdp->rehash = 0;
7229}
7230
7231static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007232hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007233{
7234 struct tblentry **pp;
7235 struct tblentry *cmdp;
7236 int c;
7237 struct cmdentry entry;
7238 char *name;
7239
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007240 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007241 clearcmdentry(0);
7242 return 0;
7243 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007244
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007245 if (*argptr == NULL) {
7246 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7247 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7248 if (cmdp->cmdtype == CMDNORMAL)
7249 printentry(cmdp);
7250 }
7251 }
7252 return 0;
7253 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007254
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007255 c = 0;
7256 while ((name = *argptr) != NULL) {
7257 cmdp = cmdlookup(name, 0);
7258 if (cmdp != NULL
7259 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007260 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7261 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007262 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007263 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007264 find_command(name, &entry, DO_ERR, pathval());
7265 if (entry.cmdtype == CMDUNKNOWN)
7266 c = 1;
7267 argptr++;
7268 }
7269 return c;
7270}
7271
7272/*
7273 * Called when a cd is done. Marks all commands so the next time they
7274 * are executed they will be rehashed.
7275 */
7276static void
7277hashcd(void)
7278{
7279 struct tblentry **pp;
7280 struct tblentry *cmdp;
7281
7282 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7283 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007284 if (cmdp->cmdtype == CMDNORMAL
7285 || (cmdp->cmdtype == CMDBUILTIN
7286 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7287 && builtinloc > 0)
7288 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007289 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007290 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007291 }
7292 }
7293}
7294
7295/*
7296 * Fix command hash table when PATH changed.
7297 * Called before PATH is changed. The argument is the new value of PATH;
7298 * pathval() still returns the old value at this point.
7299 * Called with interrupts off.
7300 */
7301static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007302changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007303{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007304 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007305 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007306 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007307 int idx_bltin;
7308
7309 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007310 firstchange = 9999; /* assume no change */
7311 idx = 0;
7312 idx_bltin = -1;
7313 for (;;) {
7314 if (*old != *new) {
7315 firstchange = idx;
7316 if ((*old == '\0' && *new == ':')
7317 || (*old == ':' && *new == '\0'))
7318 firstchange++;
7319 old = new; /* ignore subsequent differences */
7320 }
7321 if (*new == '\0')
7322 break;
7323 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7324 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007325 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007326 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007327 new++, old++;
7328 }
7329 if (builtinloc < 0 && idx_bltin >= 0)
7330 builtinloc = idx_bltin; /* zap builtins */
7331 if (builtinloc >= 0 && idx_bltin < 0)
7332 firstchange = 0;
7333 clearcmdentry(firstchange);
7334 builtinloc = idx_bltin;
7335}
7336
7337#define TEOF 0
7338#define TNL 1
7339#define TREDIR 2
7340#define TWORD 3
7341#define TSEMI 4
7342#define TBACKGND 5
7343#define TAND 6
7344#define TOR 7
7345#define TPIPE 8
7346#define TLP 9
7347#define TRP 10
7348#define TENDCASE 11
7349#define TENDBQUOTE 12
7350#define TNOT 13
7351#define TCASE 14
7352#define TDO 15
7353#define TDONE 16
7354#define TELIF 17
7355#define TELSE 18
7356#define TESAC 19
7357#define TFI 20
7358#define TFOR 21
7359#define TIF 22
7360#define TIN 23
7361#define TTHEN 24
7362#define TUNTIL 25
7363#define TWHILE 26
7364#define TBEGIN 27
7365#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007366typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007367
7368/* first char is indicating which tokens mark the end of a list */
7369static const char *const tokname_array[] = {
7370 "\1end of file",
7371 "\0newline",
7372 "\0redirection",
7373 "\0word",
7374 "\0;",
7375 "\0&",
7376 "\0&&",
7377 "\0||",
7378 "\0|",
7379 "\0(",
7380 "\1)",
7381 "\1;;",
7382 "\1`",
7383#define KWDOFFSET 13
7384 /* the following are keywords */
7385 "\0!",
7386 "\0case",
7387 "\1do",
7388 "\1done",
7389 "\1elif",
7390 "\1else",
7391 "\1esac",
7392 "\1fi",
7393 "\0for",
7394 "\0if",
7395 "\0in",
7396 "\1then",
7397 "\0until",
7398 "\0while",
7399 "\0{",
7400 "\1}",
7401};
7402
7403static const char *
7404tokname(int tok)
7405{
7406 static char buf[16];
7407
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007408//try this:
7409//if (tok < TSEMI) return tokname_array[tok] + 1;
7410//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7411//return buf;
7412
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007413 if (tok >= TSEMI)
7414 buf[0] = '"';
7415 sprintf(buf + (tok >= TSEMI), "%s%c",
7416 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7417 return buf;
7418}
7419
7420/* Wrapper around strcmp for qsort/bsearch/... */
7421static int
7422pstrcmp(const void *a, const void *b)
7423{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007424 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007425}
7426
7427static const char *const *
7428findkwd(const char *s)
7429{
7430 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007431 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7432 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007433}
7434
7435/*
7436 * Locate and print what a word is...
7437 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007438static int
7439describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007440{
7441 struct cmdentry entry;
7442 struct tblentry *cmdp;
7443#if ENABLE_ASH_ALIAS
7444 const struct alias *ap;
7445#endif
7446 const char *path = pathval();
7447
7448 if (describe_command_verbose) {
7449 out1str(command);
7450 }
7451
7452 /* First look at the keywords */
7453 if (findkwd(command)) {
7454 out1str(describe_command_verbose ? " is a shell keyword" : command);
7455 goto out;
7456 }
7457
7458#if ENABLE_ASH_ALIAS
7459 /* Then look at the aliases */
7460 ap = lookupalias(command, 0);
7461 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007462 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007463 out1str("alias ");
7464 printalias(ap);
7465 return 0;
7466 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007467 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007468 goto out;
7469 }
7470#endif
7471 /* Then check if it is a tracked alias */
7472 cmdp = cmdlookup(command, 0);
7473 if (cmdp != NULL) {
7474 entry.cmdtype = cmdp->cmdtype;
7475 entry.u = cmdp->param;
7476 } else {
7477 /* Finally use brute force */
7478 find_command(command, &entry, DO_ABS, path);
7479 }
7480
7481 switch (entry.cmdtype) {
7482 case CMDNORMAL: {
7483 int j = entry.u.index;
7484 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007485 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007486 p = command;
7487 } else {
7488 do {
7489 p = padvance(&path, command);
7490 stunalloc(p);
7491 } while (--j >= 0);
7492 }
7493 if (describe_command_verbose) {
7494 out1fmt(" is%s %s",
7495 (cmdp ? " a tracked alias for" : nullstr), p
7496 );
7497 } else {
7498 out1str(p);
7499 }
7500 break;
7501 }
7502
7503 case CMDFUNCTION:
7504 if (describe_command_verbose) {
7505 out1str(" is a shell function");
7506 } else {
7507 out1str(command);
7508 }
7509 break;
7510
7511 case CMDBUILTIN:
7512 if (describe_command_verbose) {
7513 out1fmt(" is a %sshell builtin",
7514 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7515 "special " : nullstr
7516 );
7517 } else {
7518 out1str(command);
7519 }
7520 break;
7521
7522 default:
7523 if (describe_command_verbose) {
7524 out1str(": not found\n");
7525 }
7526 return 127;
7527 }
7528 out:
7529 outstr("\n", stdout);
7530 return 0;
7531}
7532
7533static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007534typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007535{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007536 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007537 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007538 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007539
Denis Vlasenko46846e22007-05-20 13:08:31 +00007540 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007541 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007542 i++;
7543 verbose = 0;
7544 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007545 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007546 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007547 }
7548 return err;
7549}
7550
7551#if ENABLE_ASH_CMDCMD
7552static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007553commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007554{
7555 int c;
7556 enum {
7557 VERIFY_BRIEF = 1,
7558 VERIFY_VERBOSE = 2,
7559 } verify = 0;
7560
7561 while ((c = nextopt("pvV")) != '\0')
7562 if (c == 'V')
7563 verify |= VERIFY_VERBOSE;
7564 else if (c == 'v')
7565 verify |= VERIFY_BRIEF;
7566#if DEBUG
7567 else if (c != 'p')
7568 abort();
7569#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007570 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7571 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007572 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007573 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007574
7575 return 0;
7576}
7577#endif
7578
7579
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007580/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007581
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007582static int funcblocksize; /* size of structures in function */
7583static int funcstringsize; /* size of strings in node */
7584static void *funcblock; /* block to allocate function from */
7585static char *funcstring; /* block to allocate strings from */
7586
Eric Andersencb57d552001-06-28 07:25:16 +00007587/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007588#define EV_EXIT 01 /* exit after evaluating tree */
7589#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7590#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007591
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007592static const short nodesize[26] = {
7593 SHELL_ALIGN(sizeof(struct ncmd)),
7594 SHELL_ALIGN(sizeof(struct npipe)),
7595 SHELL_ALIGN(sizeof(struct nredir)),
7596 SHELL_ALIGN(sizeof(struct nredir)),
7597 SHELL_ALIGN(sizeof(struct nredir)),
7598 SHELL_ALIGN(sizeof(struct nbinary)),
7599 SHELL_ALIGN(sizeof(struct nbinary)),
7600 SHELL_ALIGN(sizeof(struct nbinary)),
7601 SHELL_ALIGN(sizeof(struct nif)),
7602 SHELL_ALIGN(sizeof(struct nbinary)),
7603 SHELL_ALIGN(sizeof(struct nbinary)),
7604 SHELL_ALIGN(sizeof(struct nfor)),
7605 SHELL_ALIGN(sizeof(struct ncase)),
7606 SHELL_ALIGN(sizeof(struct nclist)),
7607 SHELL_ALIGN(sizeof(struct narg)),
7608 SHELL_ALIGN(sizeof(struct narg)),
7609 SHELL_ALIGN(sizeof(struct nfile)),
7610 SHELL_ALIGN(sizeof(struct nfile)),
7611 SHELL_ALIGN(sizeof(struct nfile)),
7612 SHELL_ALIGN(sizeof(struct nfile)),
7613 SHELL_ALIGN(sizeof(struct nfile)),
7614 SHELL_ALIGN(sizeof(struct ndup)),
7615 SHELL_ALIGN(sizeof(struct ndup)),
7616 SHELL_ALIGN(sizeof(struct nhere)),
7617 SHELL_ALIGN(sizeof(struct nhere)),
7618 SHELL_ALIGN(sizeof(struct nnot)),
7619};
7620
7621static void calcsize(union node *n);
7622
7623static void
7624sizenodelist(struct nodelist *lp)
7625{
7626 while (lp) {
7627 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7628 calcsize(lp->n);
7629 lp = lp->next;
7630 }
7631}
7632
7633static void
7634calcsize(union node *n)
7635{
7636 if (n == NULL)
7637 return;
7638 funcblocksize += nodesize[n->type];
7639 switch (n->type) {
7640 case NCMD:
7641 calcsize(n->ncmd.redirect);
7642 calcsize(n->ncmd.args);
7643 calcsize(n->ncmd.assign);
7644 break;
7645 case NPIPE:
7646 sizenodelist(n->npipe.cmdlist);
7647 break;
7648 case NREDIR:
7649 case NBACKGND:
7650 case NSUBSHELL:
7651 calcsize(n->nredir.redirect);
7652 calcsize(n->nredir.n);
7653 break;
7654 case NAND:
7655 case NOR:
7656 case NSEMI:
7657 case NWHILE:
7658 case NUNTIL:
7659 calcsize(n->nbinary.ch2);
7660 calcsize(n->nbinary.ch1);
7661 break;
7662 case NIF:
7663 calcsize(n->nif.elsepart);
7664 calcsize(n->nif.ifpart);
7665 calcsize(n->nif.test);
7666 break;
7667 case NFOR:
7668 funcstringsize += strlen(n->nfor.var) + 1;
7669 calcsize(n->nfor.body);
7670 calcsize(n->nfor.args);
7671 break;
7672 case NCASE:
7673 calcsize(n->ncase.cases);
7674 calcsize(n->ncase.expr);
7675 break;
7676 case NCLIST:
7677 calcsize(n->nclist.body);
7678 calcsize(n->nclist.pattern);
7679 calcsize(n->nclist.next);
7680 break;
7681 case NDEFUN:
7682 case NARG:
7683 sizenodelist(n->narg.backquote);
7684 funcstringsize += strlen(n->narg.text) + 1;
7685 calcsize(n->narg.next);
7686 break;
7687 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007688#if ENABLE_ASH_BASH_COMPAT
7689 case NTO2:
7690#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007691 case NCLOBBER:
7692 case NFROM:
7693 case NFROMTO:
7694 case NAPPEND:
7695 calcsize(n->nfile.fname);
7696 calcsize(n->nfile.next);
7697 break;
7698 case NTOFD:
7699 case NFROMFD:
7700 calcsize(n->ndup.vname);
7701 calcsize(n->ndup.next);
7702 break;
7703 case NHERE:
7704 case NXHERE:
7705 calcsize(n->nhere.doc);
7706 calcsize(n->nhere.next);
7707 break;
7708 case NNOT:
7709 calcsize(n->nnot.com);
7710 break;
7711 };
7712}
7713
7714static char *
7715nodeckstrdup(char *s)
7716{
7717 char *rtn = funcstring;
7718
7719 strcpy(funcstring, s);
7720 funcstring += strlen(s) + 1;
7721 return rtn;
7722}
7723
7724static union node *copynode(union node *);
7725
7726static struct nodelist *
7727copynodelist(struct nodelist *lp)
7728{
7729 struct nodelist *start;
7730 struct nodelist **lpp;
7731
7732 lpp = &start;
7733 while (lp) {
7734 *lpp = funcblock;
7735 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7736 (*lpp)->n = copynode(lp->n);
7737 lp = lp->next;
7738 lpp = &(*lpp)->next;
7739 }
7740 *lpp = NULL;
7741 return start;
7742}
7743
7744static union node *
7745copynode(union node *n)
7746{
7747 union node *new;
7748
7749 if (n == NULL)
7750 return NULL;
7751 new = funcblock;
7752 funcblock = (char *) funcblock + nodesize[n->type];
7753
7754 switch (n->type) {
7755 case NCMD:
7756 new->ncmd.redirect = copynode(n->ncmd.redirect);
7757 new->ncmd.args = copynode(n->ncmd.args);
7758 new->ncmd.assign = copynode(n->ncmd.assign);
7759 break;
7760 case NPIPE:
7761 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007762 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007763 break;
7764 case NREDIR:
7765 case NBACKGND:
7766 case NSUBSHELL:
7767 new->nredir.redirect = copynode(n->nredir.redirect);
7768 new->nredir.n = copynode(n->nredir.n);
7769 break;
7770 case NAND:
7771 case NOR:
7772 case NSEMI:
7773 case NWHILE:
7774 case NUNTIL:
7775 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7776 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7777 break;
7778 case NIF:
7779 new->nif.elsepart = copynode(n->nif.elsepart);
7780 new->nif.ifpart = copynode(n->nif.ifpart);
7781 new->nif.test = copynode(n->nif.test);
7782 break;
7783 case NFOR:
7784 new->nfor.var = nodeckstrdup(n->nfor.var);
7785 new->nfor.body = copynode(n->nfor.body);
7786 new->nfor.args = copynode(n->nfor.args);
7787 break;
7788 case NCASE:
7789 new->ncase.cases = copynode(n->ncase.cases);
7790 new->ncase.expr = copynode(n->ncase.expr);
7791 break;
7792 case NCLIST:
7793 new->nclist.body = copynode(n->nclist.body);
7794 new->nclist.pattern = copynode(n->nclist.pattern);
7795 new->nclist.next = copynode(n->nclist.next);
7796 break;
7797 case NDEFUN:
7798 case NARG:
7799 new->narg.backquote = copynodelist(n->narg.backquote);
7800 new->narg.text = nodeckstrdup(n->narg.text);
7801 new->narg.next = copynode(n->narg.next);
7802 break;
7803 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007804#if ENABLE_ASH_BASH_COMPAT
7805 case NTO2:
7806#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007807 case NCLOBBER:
7808 case NFROM:
7809 case NFROMTO:
7810 case NAPPEND:
7811 new->nfile.fname = copynode(n->nfile.fname);
7812 new->nfile.fd = n->nfile.fd;
7813 new->nfile.next = copynode(n->nfile.next);
7814 break;
7815 case NTOFD:
7816 case NFROMFD:
7817 new->ndup.vname = copynode(n->ndup.vname);
7818 new->ndup.dupfd = n->ndup.dupfd;
7819 new->ndup.fd = n->ndup.fd;
7820 new->ndup.next = copynode(n->ndup.next);
7821 break;
7822 case NHERE:
7823 case NXHERE:
7824 new->nhere.doc = copynode(n->nhere.doc);
7825 new->nhere.fd = n->nhere.fd;
7826 new->nhere.next = copynode(n->nhere.next);
7827 break;
7828 case NNOT:
7829 new->nnot.com = copynode(n->nnot.com);
7830 break;
7831 };
7832 new->type = n->type;
7833 return new;
7834}
7835
7836/*
7837 * Make a copy of a parse tree.
7838 */
7839static struct funcnode *
7840copyfunc(union node *n)
7841{
7842 struct funcnode *f;
7843 size_t blocksize;
7844
7845 funcblocksize = offsetof(struct funcnode, n);
7846 funcstringsize = 0;
7847 calcsize(n);
7848 blocksize = funcblocksize;
7849 f = ckmalloc(blocksize + funcstringsize);
7850 funcblock = (char *) f + offsetof(struct funcnode, n);
7851 funcstring = (char *) f + blocksize;
7852 copynode(n);
7853 f->count = 0;
7854 return f;
7855}
7856
7857/*
7858 * Define a shell function.
7859 */
7860static void
7861defun(char *name, union node *func)
7862{
7863 struct cmdentry entry;
7864
7865 INT_OFF;
7866 entry.cmdtype = CMDFUNCTION;
7867 entry.u.func = copyfunc(func);
7868 addcmdentry(name, &entry);
7869 INT_ON;
7870}
7871
7872static int evalskip; /* set if we are skipping commands */
7873/* reasons for skipping commands (see comment on breakcmd routine) */
7874#define SKIPBREAK (1 << 0)
7875#define SKIPCONT (1 << 1)
7876#define SKIPFUNC (1 << 2)
7877#define SKIPFILE (1 << 3)
7878#define SKIPEVAL (1 << 4)
7879static int skipcount; /* number of levels to skip */
7880static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007881static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007882
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007883/* forward decl way out to parsing code - dotrap needs it */
7884static int evalstring(char *s, int mask);
7885
7886/*
7887 * Called to execute a trap. Perhaps we should avoid entering new trap
7888 * handlers while we are executing a trap handler.
7889 */
7890static int
7891dotrap(void)
7892{
7893 char *p;
7894 char *q;
7895 int i;
7896 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007897 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007898
7899 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007900 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007901 xbarrier();
7902
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007903 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007904 if (!*q)
7905 continue;
7906 *q = '\0';
7907
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007908 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007909 if (!p)
7910 continue;
7911 skip = evalstring(p, SKIPEVAL);
7912 exitstatus = savestatus;
7913 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007914 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007915 }
7916
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007917 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007918}
7919
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007920/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007921static void evalloop(union node *, int);
7922static void evalfor(union node *, int);
7923static void evalcase(union node *, int);
7924static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007925static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007926static void evalpipe(union node *, int);
7927static void evalcommand(union node *, int);
7928static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007929static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007930
Eric Andersen62483552001-07-10 06:09:16 +00007931/*
Eric Andersenc470f442003-07-28 09:56:35 +00007932 * Evaluate a parse tree. The value is left in the global variable
7933 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007934 */
Eric Andersenc470f442003-07-28 09:56:35 +00007935static void
7936evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007937{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007938
7939 struct jmploc *volatile savehandler = exception_handler;
7940 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007941 int checkexit = 0;
7942 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007943 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007944
Eric Andersenc470f442003-07-28 09:56:35 +00007945 if (n == NULL) {
7946 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007947 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007948 }
Eric Andersenc470f442003-07-28 09:56:35 +00007949 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007950 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007951
7952 exception_handler = &jmploc;
7953 {
7954 int err = setjmp(jmploc.loc);
7955 if (err) {
7956 /* if it was a signal, check for trap handlers */
7957 if (exception == EXSIG)
7958 goto out;
7959 /* continue on the way out */
7960 exception_handler = savehandler;
7961 longjmp(exception_handler->loc, err);
7962 }
7963 }
7964
Eric Andersenc470f442003-07-28 09:56:35 +00007965 switch (n->type) {
7966 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007967#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007968 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007969 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007970 break;
7971#endif
7972 case NNOT:
7973 evaltree(n->nnot.com, EV_TESTED);
7974 status = !exitstatus;
7975 goto setstatus;
7976 case NREDIR:
7977 expredir(n->nredir.redirect);
7978 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7979 if (!status) {
7980 evaltree(n->nredir.n, flags & EV_TESTED);
7981 status = exitstatus;
7982 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007983 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00007984 goto setstatus;
7985 case NCMD:
7986 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007987 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007988 if (eflag && !(flags & EV_TESTED))
7989 checkexit = ~0;
7990 goto calleval;
7991 case NFOR:
7992 evalfn = evalfor;
7993 goto calleval;
7994 case NWHILE:
7995 case NUNTIL:
7996 evalfn = evalloop;
7997 goto calleval;
7998 case NSUBSHELL:
7999 case NBACKGND:
8000 evalfn = evalsubshell;
8001 goto calleval;
8002 case NPIPE:
8003 evalfn = evalpipe;
8004 goto checkexit;
8005 case NCASE:
8006 evalfn = evalcase;
8007 goto calleval;
8008 case NAND:
8009 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008010 case NSEMI: {
8011
Eric Andersenc470f442003-07-28 09:56:35 +00008012#if NAND + 1 != NOR
8013#error NAND + 1 != NOR
8014#endif
8015#if NOR + 1 != NSEMI
8016#error NOR + 1 != NSEMI
8017#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008018 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008019 evaltree(
8020 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008021 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008022 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008023 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008024 break;
8025 if (!evalskip) {
8026 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008027 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008028 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008029 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008030 evalfn(n, flags);
8031 break;
8032 }
8033 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008034 }
Eric Andersenc470f442003-07-28 09:56:35 +00008035 case NIF:
8036 evaltree(n->nif.test, EV_TESTED);
8037 if (evalskip)
8038 break;
8039 if (exitstatus == 0) {
8040 n = n->nif.ifpart;
8041 goto evaln;
8042 } else if (n->nif.elsepart) {
8043 n = n->nif.elsepart;
8044 goto evaln;
8045 }
8046 goto success;
8047 case NDEFUN:
8048 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008049 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008050 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008051 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008052 exitstatus = status;
8053 break;
8054 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008055
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008056 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008057 exception_handler = savehandler;
8058 out1:
8059 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008060 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008061 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008062 goto exexit;
8063
8064 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008065 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008066 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008067 }
Eric Andersen62483552001-07-10 06:09:16 +00008068}
8069
Eric Andersenc470f442003-07-28 09:56:35 +00008070#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8071static
8072#endif
8073void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8074
Eric Andersenc470f442003-07-28 09:56:35 +00008075static void
8076evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008077{
8078 int status;
8079
8080 loopnest++;
8081 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008082 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008083 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008084 int i;
8085
Eric Andersencb57d552001-06-28 07:25:16 +00008086 evaltree(n->nbinary.ch1, EV_TESTED);
8087 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008088 skipping:
8089 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008090 evalskip = 0;
8091 continue;
8092 }
8093 if (evalskip == SKIPBREAK && --skipcount <= 0)
8094 evalskip = 0;
8095 break;
8096 }
Eric Andersenc470f442003-07-28 09:56:35 +00008097 i = exitstatus;
8098 if (n->type != NWHILE)
8099 i = !i;
8100 if (i != 0)
8101 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008102 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008103 status = exitstatus;
8104 if (evalskip)
8105 goto skipping;
8106 }
8107 loopnest--;
8108 exitstatus = status;
8109}
8110
Eric Andersenc470f442003-07-28 09:56:35 +00008111static void
8112evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008113{
8114 struct arglist arglist;
8115 union node *argp;
8116 struct strlist *sp;
8117 struct stackmark smark;
8118
8119 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008120 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008121 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008122 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008123 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008124 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008125 if (evalskip)
8126 goto out;
8127 }
8128 *arglist.lastp = NULL;
8129
8130 exitstatus = 0;
8131 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008132 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008133 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008134 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008135 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008136 if (evalskip) {
8137 if (evalskip == SKIPCONT && --skipcount <= 0) {
8138 evalskip = 0;
8139 continue;
8140 }
8141 if (evalskip == SKIPBREAK && --skipcount <= 0)
8142 evalskip = 0;
8143 break;
8144 }
8145 }
8146 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008147 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008148 popstackmark(&smark);
8149}
8150
Eric Andersenc470f442003-07-28 09:56:35 +00008151static void
8152evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008153{
8154 union node *cp;
8155 union node *patp;
8156 struct arglist arglist;
8157 struct stackmark smark;
8158
8159 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008160 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008161 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008162 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008163 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008164 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8165 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008166 if (casematch(patp, arglist.list->text)) {
8167 if (evalskip == 0) {
8168 evaltree(cp->nclist.body, flags);
8169 }
8170 goto out;
8171 }
8172 }
8173 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008174 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008175 popstackmark(&smark);
8176}
8177
Eric Andersenc470f442003-07-28 09:56:35 +00008178/*
8179 * Kick off a subshell to evaluate a tree.
8180 */
Eric Andersenc470f442003-07-28 09:56:35 +00008181static void
8182evalsubshell(union node *n, int flags)
8183{
8184 struct job *jp;
8185 int backgnd = (n->type == NBACKGND);
8186 int status;
8187
8188 expredir(n->nredir.redirect);
8189 if (!backgnd && flags & EV_EXIT && !trap[0])
8190 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008191 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008192 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008193 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008194 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008195 flags |= EV_EXIT;
8196 if (backgnd)
8197 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008198 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008199 redirect(n->nredir.redirect, 0);
8200 evaltreenr(n->nredir.n, flags);
8201 /* never returns */
8202 }
8203 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008204 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008205 status = waitforjob(jp);
8206 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008207 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008208}
8209
Eric Andersenc470f442003-07-28 09:56:35 +00008210/*
8211 * Compute the names of the files in a redirection list.
8212 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008213static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008214static void
8215expredir(union node *n)
8216{
8217 union node *redir;
8218
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008219 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008220 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008221
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008222 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008223 fn.lastp = &fn.list;
8224 switch (redir->type) {
8225 case NFROMTO:
8226 case NFROM:
8227 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008228#if ENABLE_ASH_BASH_COMPAT
8229 case NTO2:
8230#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008231 case NCLOBBER:
8232 case NAPPEND:
8233 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008234#if ENABLE_ASH_BASH_COMPAT
8235 store_expfname:
8236#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008237 redir->nfile.expfname = fn.list->text;
8238 break;
8239 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008240 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008241 if (redir->ndup.vname) {
8242 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008243 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008244 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008245#if ENABLE_ASH_BASH_COMPAT
8246//FIXME: we used expandarg with different args!
8247 if (!isdigit_str9(fn.list->text)) {
8248 /* >&file, not >&fd */
8249 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8250 ash_msg_and_raise_error("redir error");
8251 redir->type = NTO2;
8252 goto store_expfname;
8253 }
8254#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008255 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008256 }
8257 break;
8258 }
8259 }
8260}
8261
Eric Andersencb57d552001-06-28 07:25:16 +00008262/*
Eric Andersencb57d552001-06-28 07:25:16 +00008263 * Evaluate a pipeline. All the processes in the pipeline are children
8264 * of the process creating the pipeline. (This differs from some versions
8265 * of the shell, which make the last process in a pipeline the parent
8266 * of all the rest.)
8267 */
Eric Andersenc470f442003-07-28 09:56:35 +00008268static void
8269evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008270{
8271 struct job *jp;
8272 struct nodelist *lp;
8273 int pipelen;
8274 int prevfd;
8275 int pip[2];
8276
Eric Andersenc470f442003-07-28 09:56:35 +00008277 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008278 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008279 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008280 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008281 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008282 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008283 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008284 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008285 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008286 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008287 pip[1] = -1;
8288 if (lp->next) {
8289 if (pipe(pip) < 0) {
8290 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008291 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008292 }
8293 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008294 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008295 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008296 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008297 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008298 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008299 if (prevfd > 0) {
8300 dup2(prevfd, 0);
8301 close(prevfd);
8302 }
8303 if (pip[1] > 1) {
8304 dup2(pip[1], 1);
8305 close(pip[1]);
8306 }
Eric Andersenc470f442003-07-28 09:56:35 +00008307 evaltreenr(lp->n, flags);
8308 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008309 }
8310 if (prevfd >= 0)
8311 close(prevfd);
8312 prevfd = pip[0];
8313 close(pip[1]);
8314 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008315 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008316 exitstatus = waitforjob(jp);
8317 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008318 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008319 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008320}
8321
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008322/*
8323 * Controls whether the shell is interactive or not.
8324 */
8325static void
8326setinteractive(int on)
8327{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008328 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008329
8330 if (++on == is_interactive)
8331 return;
8332 is_interactive = on;
8333 setsignal(SIGINT);
8334 setsignal(SIGQUIT);
8335 setsignal(SIGTERM);
8336#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8337 if (is_interactive > 1) {
8338 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008339 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008340
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008341 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008342 out1fmt(
8343 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008344 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008345 "Enter 'help' for a list of built-in commands."
8346 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008347 bb_banner);
8348 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008349 }
8350 }
8351#endif
8352}
8353
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008354static void
8355optschanged(void)
8356{
8357#if DEBUG
8358 opentrace();
8359#endif
8360 setinteractive(iflag);
8361 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008362#if ENABLE_FEATURE_EDITING_VI
8363 if (viflag)
8364 line_input_state->flags |= VI_MODE;
8365 else
8366 line_input_state->flags &= ~VI_MODE;
8367#else
8368 viflag = 0; /* forcibly keep the option off */
8369#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008370}
8371
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008372static struct localvar *localvars;
8373
8374/*
8375 * Called after a function returns.
8376 * Interrupts must be off.
8377 */
8378static void
8379poplocalvars(void)
8380{
8381 struct localvar *lvp;
8382 struct var *vp;
8383
8384 while ((lvp = localvars) != NULL) {
8385 localvars = lvp->next;
8386 vp = lvp->vp;
8387 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8388 if (vp == NULL) { /* $- saved */
8389 memcpy(optlist, lvp->text, sizeof(optlist));
8390 free((char*)lvp->text);
8391 optschanged();
8392 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8393 unsetvar(vp->text);
8394 } else {
8395 if (vp->func)
8396 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8397 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8398 free((char*)vp->text);
8399 vp->flags = lvp->flags;
8400 vp->text = lvp->text;
8401 }
8402 free(lvp);
8403 }
8404}
8405
8406static int
8407evalfun(struct funcnode *func, int argc, char **argv, int flags)
8408{
8409 volatile struct shparam saveparam;
8410 struct localvar *volatile savelocalvars;
8411 struct jmploc *volatile savehandler;
8412 struct jmploc jmploc;
8413 int e;
8414
8415 saveparam = shellparam;
8416 savelocalvars = localvars;
8417 e = setjmp(jmploc.loc);
8418 if (e) {
8419 goto funcdone;
8420 }
8421 INT_OFF;
8422 savehandler = exception_handler;
8423 exception_handler = &jmploc;
8424 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008425 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008426 func->count++;
8427 funcnest++;
8428 INT_ON;
8429 shellparam.nparam = argc - 1;
8430 shellparam.p = argv + 1;
8431#if ENABLE_ASH_GETOPTS
8432 shellparam.optind = 1;
8433 shellparam.optoff = -1;
8434#endif
8435 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008436 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008437 INT_OFF;
8438 funcnest--;
8439 freefunc(func);
8440 poplocalvars();
8441 localvars = savelocalvars;
8442 freeparam(&shellparam);
8443 shellparam = saveparam;
8444 exception_handler = savehandler;
8445 INT_ON;
8446 evalskip &= ~SKIPFUNC;
8447 return e;
8448}
8449
Denis Vlasenko131ae172007-02-18 13:00:19 +00008450#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008451static char **
8452parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008453{
8454 char *cp, c;
8455
8456 for (;;) {
8457 cp = *++argv;
8458 if (!cp)
8459 return 0;
8460 if (*cp++ != '-')
8461 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008462 c = *cp++;
8463 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008464 break;
8465 if (c == '-' && !*cp) {
8466 argv++;
8467 break;
8468 }
8469 do {
8470 switch (c) {
8471 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008472 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008473 break;
8474 default:
8475 /* run 'typecmd' for other options */
8476 return 0;
8477 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008478 c = *cp++;
8479 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008480 }
8481 return argv;
8482}
8483#endif
8484
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008485/*
8486 * Make a variable a local variable. When a variable is made local, it's
8487 * value and flags are saved in a localvar structure. The saved values
8488 * will be restored when the shell function returns. We handle the name
8489 * "-" as a special case.
8490 */
8491static void
8492mklocal(char *name)
8493{
8494 struct localvar *lvp;
8495 struct var **vpp;
8496 struct var *vp;
8497
8498 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008499 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008500 if (LONE_DASH(name)) {
8501 char *p;
8502 p = ckmalloc(sizeof(optlist));
8503 lvp->text = memcpy(p, optlist, sizeof(optlist));
8504 vp = NULL;
8505 } else {
8506 char *eq;
8507
8508 vpp = hashvar(name);
8509 vp = *findvar(vpp, name);
8510 eq = strchr(name, '=');
8511 if (vp == NULL) {
8512 if (eq)
8513 setvareq(name, VSTRFIXED);
8514 else
8515 setvar(name, NULL, VSTRFIXED);
8516 vp = *vpp; /* the new variable */
8517 lvp->flags = VUNSET;
8518 } else {
8519 lvp->text = vp->text;
8520 lvp->flags = vp->flags;
8521 vp->flags |= VSTRFIXED|VTEXTFIXED;
8522 if (eq)
8523 setvareq(name, 0);
8524 }
8525 }
8526 lvp->vp = vp;
8527 lvp->next = localvars;
8528 localvars = lvp;
8529 INT_ON;
8530}
8531
8532/*
8533 * The "local" command.
8534 */
8535static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008536localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008537{
8538 char *name;
8539
8540 argv = argptr;
8541 while ((name = *argv++) != NULL) {
8542 mklocal(name);
8543 }
8544 return 0;
8545}
8546
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008547static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008548falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008549{
8550 return 1;
8551}
8552
8553static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008554truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008555{
8556 return 0;
8557}
8558
8559static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008560execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008561{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008562 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008563 iflag = 0; /* exit on error */
8564 mflag = 0;
8565 optschanged();
8566 shellexec(argv + 1, pathval(), 0);
8567 }
8568 return 0;
8569}
8570
8571/*
8572 * The return command.
8573 */
8574static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008575returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008576{
8577 /*
8578 * If called outside a function, do what ksh does;
8579 * skip the rest of the file.
8580 */
8581 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8582 return argv[1] ? number(argv[1]) : exitstatus;
8583}
8584
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008585/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008586static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008587static int dotcmd(int, char **);
8588static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008589static int exitcmd(int, char **);
8590static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008591#if ENABLE_ASH_GETOPTS
8592static int getoptscmd(int, char **);
8593#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008594#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008595static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008596#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008597#if ENABLE_ASH_MATH_SUPPORT
8598static int letcmd(int, char **);
8599#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008600static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008601static int setcmd(int, char **);
8602static int shiftcmd(int, char **);
8603static int timescmd(int, char **);
8604static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008605static int umaskcmd(int, char **);
8606static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008607static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008608
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008609#define BUILTIN_NOSPEC "0"
8610#define BUILTIN_SPECIAL "1"
8611#define BUILTIN_REGULAR "2"
8612#define BUILTIN_SPEC_REG "3"
8613#define BUILTIN_ASSIGN "4"
8614#define BUILTIN_SPEC_ASSG "5"
8615#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008616#define BUILTIN_SPEC_REG_ASSG "7"
8617
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008618/* We do not handle [[ expr ]] bashism bash-compatibly,
8619 * we make it a synonym of [ expr ].
8620 * Basically, word splitting and pathname expansion should NOT be performed
8621 * Examples:
8622 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8623 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8624 * Additional operators:
8625 * || and && should work as -o and -a
8626 * =~ regexp match
8627 * Apart from the above, [[ expr ]] should work as [ expr ]
8628 */
8629
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008630#define echocmd echo_main
8631#define printfcmd printf_main
8632#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008633
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008634/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008635static const struct builtincmd builtintab[] = {
8636 { BUILTIN_SPEC_REG ".", dotcmd },
8637 { BUILTIN_SPEC_REG ":", truecmd },
8638#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008639 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008640#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008641 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008642#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008643#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008644#if ENABLE_ASH_ALIAS
8645 { BUILTIN_REG_ASSG "alias", aliascmd },
8646#endif
8647#if JOBS
8648 { BUILTIN_REGULAR "bg", fg_bgcmd },
8649#endif
8650 { BUILTIN_SPEC_REG "break", breakcmd },
8651 { BUILTIN_REGULAR "cd", cdcmd },
8652 { BUILTIN_NOSPEC "chdir", cdcmd },
8653#if ENABLE_ASH_CMDCMD
8654 { BUILTIN_REGULAR "command", commandcmd },
8655#endif
8656 { BUILTIN_SPEC_REG "continue", breakcmd },
8657#if ENABLE_ASH_BUILTIN_ECHO
8658 { BUILTIN_REGULAR "echo", echocmd },
8659#endif
8660 { BUILTIN_SPEC_REG "eval", evalcmd },
8661 { BUILTIN_SPEC_REG "exec", execcmd },
8662 { BUILTIN_SPEC_REG "exit", exitcmd },
8663 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8664 { BUILTIN_REGULAR "false", falsecmd },
8665#if JOBS
8666 { BUILTIN_REGULAR "fg", fg_bgcmd },
8667#endif
8668#if ENABLE_ASH_GETOPTS
8669 { BUILTIN_REGULAR "getopts", getoptscmd },
8670#endif
8671 { BUILTIN_NOSPEC "hash", hashcmd },
8672#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8673 { BUILTIN_NOSPEC "help", helpcmd },
8674#endif
8675#if JOBS
8676 { BUILTIN_REGULAR "jobs", jobscmd },
8677 { BUILTIN_REGULAR "kill", killcmd },
8678#endif
8679#if ENABLE_ASH_MATH_SUPPORT
8680 { BUILTIN_NOSPEC "let", letcmd },
8681#endif
8682 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008683#if ENABLE_ASH_BUILTIN_PRINTF
8684 { BUILTIN_REGULAR "printf", printfcmd },
8685#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008686 { BUILTIN_NOSPEC "pwd", pwdcmd },
8687 { BUILTIN_REGULAR "read", readcmd },
8688 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8689 { BUILTIN_SPEC_REG "return", returncmd },
8690 { BUILTIN_SPEC_REG "set", setcmd },
8691 { BUILTIN_SPEC_REG "shift", shiftcmd },
8692 { BUILTIN_SPEC_REG "source", dotcmd },
8693#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008694 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008695#endif
8696 { BUILTIN_SPEC_REG "times", timescmd },
8697 { BUILTIN_SPEC_REG "trap", trapcmd },
8698 { BUILTIN_REGULAR "true", truecmd },
8699 { BUILTIN_NOSPEC "type", typecmd },
8700 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8701 { BUILTIN_REGULAR "umask", umaskcmd },
8702#if ENABLE_ASH_ALIAS
8703 { BUILTIN_REGULAR "unalias", unaliascmd },
8704#endif
8705 { BUILTIN_SPEC_REG "unset", unsetcmd },
8706 { BUILTIN_REGULAR "wait", waitcmd },
8707};
8708
Denis Vlasenko80591b02008-03-25 07:49:43 +00008709/* Should match the above table! */
8710#define COMMANDCMD (builtintab + \
8711 2 + \
8712 1 * ENABLE_ASH_BUILTIN_TEST + \
8713 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8714 1 * ENABLE_ASH_ALIAS + \
8715 1 * ENABLE_ASH_JOB_CONTROL + \
8716 3)
8717#define EXECCMD (builtintab + \
8718 2 + \
8719 1 * ENABLE_ASH_BUILTIN_TEST + \
8720 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8721 1 * ENABLE_ASH_ALIAS + \
8722 1 * ENABLE_ASH_JOB_CONTROL + \
8723 3 + \
8724 1 * ENABLE_ASH_CMDCMD + \
8725 1 + \
8726 ENABLE_ASH_BUILTIN_ECHO + \
8727 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008728
8729/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008730 * Search the table of builtin commands.
8731 */
8732static struct builtincmd *
8733find_builtin(const char *name)
8734{
8735 struct builtincmd *bp;
8736
8737 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008738 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008739 pstrcmp
8740 );
8741 return bp;
8742}
8743
8744/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008745 * Execute a simple command.
8746 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008747static int
8748isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008749{
8750 const char *q = endofname(p);
8751 if (p == q)
8752 return 0;
8753 return *q == '=';
8754}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008755static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008756bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008757{
8758 /* Preserve exitstatus of a previous possible redirection
8759 * as POSIX mandates */
8760 return back_exitstatus;
8761}
Eric Andersenc470f442003-07-28 09:56:35 +00008762static void
8763evalcommand(union node *cmd, int flags)
8764{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008765 static const struct builtincmd null_bltin = {
8766 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008767 };
Eric Andersenc470f442003-07-28 09:56:35 +00008768 struct stackmark smark;
8769 union node *argp;
8770 struct arglist arglist;
8771 struct arglist varlist;
8772 char **argv;
8773 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008774 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008775 struct cmdentry cmdentry;
8776 struct job *jp;
8777 char *lastarg;
8778 const char *path;
8779 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008780 int status;
8781 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008782 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008783 smallint cmd_is_exec;
8784 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008785
8786 /* First expand the arguments. */
8787 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8788 setstackmark(&smark);
8789 back_exitstatus = 0;
8790
8791 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008792 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008793 varlist.lastp = &varlist.list;
8794 *varlist.lastp = NULL;
8795 arglist.lastp = &arglist.list;
8796 *arglist.lastp = NULL;
8797
8798 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008799 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008800 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8801 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8802 }
8803
Eric Andersenc470f442003-07-28 09:56:35 +00008804 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8805 struct strlist **spp;
8806
8807 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008808 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008809 expandarg(argp, &arglist, EXP_VARTILDE);
8810 else
8811 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8812
Eric Andersenc470f442003-07-28 09:56:35 +00008813 for (sp = *spp; sp; sp = sp->next)
8814 argc++;
8815 }
8816
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008817 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008818 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008819 TRACE(("evalcommand arg: %s\n", sp->text));
8820 *nargv++ = sp->text;
8821 }
8822 *nargv = NULL;
8823
8824 lastarg = NULL;
8825 if (iflag && funcnest == 0 && argc > 0)
8826 lastarg = nargv[-1];
8827
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008828 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008829 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008830 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008831
8832 path = vpath.text;
8833 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8834 struct strlist **spp;
8835 char *p;
8836
8837 spp = varlist.lastp;
8838 expandarg(argp, &varlist, EXP_VARTILDE);
8839
8840 /*
8841 * Modify the command lookup path, if a PATH= assignment
8842 * is present
8843 */
8844 p = (*spp)->text;
8845 if (varequal(p, path))
8846 path = p;
8847 }
8848
8849 /* Print the command if xflag is set. */
8850 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008851 int n;
8852 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008853
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008854 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008855 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008856
8857 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008858 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008859 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008860 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008861 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008862 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008863 p--;
8864 }
8865 }
8866 sp = arglist.list;
8867 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008868 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008869 }
8870
8871 cmd_is_exec = 0;
8872 spclbltin = -1;
8873
8874 /* Now locate the command. */
8875 if (argc) {
8876 const char *oldpath;
8877 int cmd_flag = DO_ERR;
8878
8879 path += 5;
8880 oldpath = path;
8881 for (;;) {
8882 find_command(argv[0], &cmdentry, cmd_flag, path);
8883 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008884 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008885 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008886 goto bail;
8887 }
8888
8889 /* implement bltin and command here */
8890 if (cmdentry.cmdtype != CMDBUILTIN)
8891 break;
8892 if (spclbltin < 0)
8893 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8894 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008895 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008896#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008897 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008898 path = oldpath;
8899 nargv = parse_command_args(argv, &path);
8900 if (!nargv)
8901 break;
8902 argc -= nargv - argv;
8903 argv = nargv;
8904 cmd_flag |= DO_NOFUNC;
8905 } else
8906#endif
8907 break;
8908 }
8909 }
8910
8911 if (status) {
8912 /* We have a redirection error. */
8913 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008914 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008915 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008916 exitstatus = status;
8917 goto out;
8918 }
8919
8920 /* Execute the command. */
8921 switch (cmdentry.cmdtype) {
8922 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008923#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008924 {
8925 /* find_command() encodes applet_no as (-2 - applet_no) */
8926 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008927 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008928 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008929 /* run <applet>_main() */
8930 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008931 break;
8932 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008933 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008934#endif
8935
Eric Andersenc470f442003-07-28 09:56:35 +00008936 /* Fork off a child process if necessary. */
8937 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008938 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008939 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008940 if (forkshell(jp, cmd, FORK_FG) != 0) {
8941 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008942 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008943 break;
8944 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008945 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008946 }
8947 listsetvar(varlist.list, VEXPORT|VSTACK);
8948 shellexec(argv, path, cmdentry.u.index);
8949 /* NOTREACHED */
8950
8951 case CMDBUILTIN:
8952 cmdenviron = varlist.list;
8953 if (cmdenviron) {
8954 struct strlist *list = cmdenviron;
8955 int i = VNOSET;
8956 if (spclbltin > 0 || argc == 0) {
8957 i = 0;
8958 if (cmd_is_exec && argc > 1)
8959 i = VEXPORT;
8960 }
8961 listsetvar(list, i);
8962 }
8963 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8964 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008965 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008966 if (i == EXEXIT)
8967 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008968 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008969 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008970 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008971 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008972 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008973 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008974 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008975 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008976 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008977 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008978 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008979 }
8980 break;
8981
8982 case CMDFUNCTION:
8983 listsetvar(varlist.list, 0);
8984 if (evalfun(cmdentry.u.func, argc, argv, flags))
8985 goto raise;
8986 break;
8987 }
8988
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008989 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008990 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008991 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008992 /* dsl: I think this is intended to be used to support
8993 * '_' in 'vi' command mode during line editing...
8994 * However I implemented that within libedit itself.
8995 */
8996 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008997 }
Eric Andersenc470f442003-07-28 09:56:35 +00008998 popstackmark(&smark);
8999}
9000
9001static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009002evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9003{
Eric Andersenc470f442003-07-28 09:56:35 +00009004 char *volatile savecmdname;
9005 struct jmploc *volatile savehandler;
9006 struct jmploc jmploc;
9007 int i;
9008
9009 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009010 i = setjmp(jmploc.loc);
9011 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009012 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009013 savehandler = exception_handler;
9014 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009015 commandname = argv[0];
9016 argptr = argv + 1;
9017 optptr = NULL; /* initialize nextopt */
9018 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009019 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009020 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009021 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009022 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009023 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009024// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009025 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009026
9027 return i;
9028}
9029
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009030static int
9031goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009032{
9033 return !*endofname(p);
9034}
9035
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009036
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009037/*
9038 * Search for a command. This is called before we fork so that the
9039 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009040 * the child. The check for "goodname" is an overly conservative
9041 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009042 */
Eric Andersenc470f442003-07-28 09:56:35 +00009043static void
9044prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009045{
9046 struct cmdentry entry;
9047
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009048 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9049 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009050}
9051
Eric Andersencb57d552001-06-28 07:25:16 +00009052
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009053/* ============ Builtin commands
9054 *
9055 * Builtin commands whose functions are closely tied to evaluation
9056 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009057 */
9058
9059/*
Eric Andersencb57d552001-06-28 07:25:16 +00009060 * Handle break and continue commands. Break, continue, and return are
9061 * all handled by setting the evalskip flag. The evaluation routines
9062 * above all check this flag, and if it is set they start skipping
9063 * commands rather than executing them. The variable skipcount is
9064 * the number of loops to break/continue, or the number of function
9065 * levels to return. (The latter is always 1.) It should probably
9066 * be an error to break out of more loops than exist, but it isn't
9067 * in the standard shell so we don't make it one here.
9068 */
Eric Andersenc470f442003-07-28 09:56:35 +00009069static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009070breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009071{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009072 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009073
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009074 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009075 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009076 if (n > loopnest)
9077 n = loopnest;
9078 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009079 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009080 skipcount = n;
9081 }
9082 return 0;
9083}
9084
Eric Andersenc470f442003-07-28 09:56:35 +00009085
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009086/* ============ input.c
9087 *
Eric Andersen90898442003-08-06 11:20:52 +00009088 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009089 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009090
Eric Andersenc470f442003-07-28 09:56:35 +00009091#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009092
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009093enum {
9094 INPUT_PUSH_FILE = 1,
9095 INPUT_NOFILE_OK = 2,
9096};
Eric Andersencb57d552001-06-28 07:25:16 +00009097
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009098static int plinno = 1; /* input line number */
9099/* number of characters left in input buffer */
9100static int parsenleft; /* copy of parsefile->nleft */
9101static int parselleft; /* copy of parsefile->lleft */
9102/* next character in input buffer */
9103static char *parsenextc; /* copy of parsefile->nextc */
9104
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009105static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009106/* values of checkkwd variable */
9107#define CHKALIAS 0x1
9108#define CHKKWD 0x2
9109#define CHKNL 0x4
9110
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009111static void
9112popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009113{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009114 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009115
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009116 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009117#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009118 if (sp->ap) {
9119 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9120 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009121 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009122 if (sp->string != sp->ap->val) {
9123 free(sp->string);
9124 }
9125 sp->ap->flag &= ~ALIASINUSE;
9126 if (sp->ap->flag & ALIASDEAD) {
9127 unalias(sp->ap->name);
9128 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009129 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009130#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009131 parsenextc = sp->prevstring;
9132 parsenleft = sp->prevnleft;
9133/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009134 g_parsefile->strpush = sp->prev;
9135 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009136 free(sp);
9137 INT_ON;
9138}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009139
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009140static int
9141preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009142{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009143 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009144 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009145 parsenextc = buf;
9146
Denis Vlasenko38f63192007-01-22 09:03:07 +00009147#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009148 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009149 if (!iflag || g_parsefile->fd)
9150 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009151 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009152#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009153 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009154#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009155 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9156 if (nr == 0) {
9157 /* Ctrl+C pressed */
9158 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009159 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009160 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009161 raise(SIGINT);
9162 return 1;
9163 }
Eric Andersenc470f442003-07-28 09:56:35 +00009164 goto retry;
9165 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009166 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009167 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009168 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009169 }
Eric Andersencb57d552001-06-28 07:25:16 +00009170 }
9171#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009172 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009173#endif
9174
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009175#if 0
9176/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009177 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009178 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009179 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009180 if (flags >= 0 && (flags & O_NONBLOCK)) {
9181 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009182 if (fcntl(0, F_SETFL, flags) >= 0) {
9183 out2str("sh: turning off NDELAY mode\n");
9184 goto retry;
9185 }
9186 }
9187 }
9188 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009189#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009190 return nr;
9191}
9192
9193/*
9194 * Refill the input buffer and return the next input character:
9195 *
9196 * 1) If a string was pushed back on the input, pop it;
9197 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9198 * from a string so we can't refill the buffer, return EOF.
9199 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9200 * 4) Process input up to the next newline, deleting nul characters.
9201 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009202static int
Eric Andersenc470f442003-07-28 09:56:35 +00009203preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009204{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009205 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009206 int more;
9207 char savec;
9208
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009209 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009210#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009211 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009212 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009213 return PEOA;
9214 }
Eric Andersen2870d962001-07-02 17:27:21 +00009215#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009216 popstring();
9217 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009218 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009219 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009220 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009221 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009222 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009223
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009224 more = parselleft;
9225 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009226 again:
9227 more = preadfd();
9228 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009229 parselleft = parsenleft = EOF_NLEFT;
9230 return PEOF;
9231 }
9232 }
9233
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009234 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009235
9236 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009237 for (;;) {
9238 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009239
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009240 more--;
9241 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009242
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009243 if (!c)
9244 memmove(q, q + 1, more);
9245 else {
9246 q++;
9247 if (c == '\n') {
9248 parsenleft = q - parsenextc - 1;
9249 break;
9250 }
Eric Andersencb57d552001-06-28 07:25:16 +00009251 }
9252
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009253 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009254 parsenleft = q - parsenextc - 1;
9255 if (parsenleft < 0)
9256 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009257 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009258 }
9259 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009260 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009261
9262 savec = *q;
9263 *q = '\0';
9264
9265 if (vflag) {
9266 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009267 }
9268
9269 *q = savec;
9270
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009271 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009272}
9273
Denis Vlasenko834dee72008-10-07 09:18:30 +00009274#define pgetc_as_macro() (--parsenleft >= 0 ? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009275static int
9276pgetc(void)
9277{
9278 return pgetc_as_macro();
9279}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009280
9281#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009282#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009283#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009284#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009285#endif
9286
9287/*
9288 * Same as pgetc(), but ignores PEOA.
9289 */
9290#if ENABLE_ASH_ALIAS
9291static int
9292pgetc2(void)
9293{
9294 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009295 do {
Denis Vlasenko834dee72008-10-07 09:18:30 +00009296 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009297 } while (c == PEOA);
9298 return c;
9299}
9300#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009301#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009302#endif
9303
9304/*
9305 * Read a line from the script.
9306 */
9307static char *
9308pfgets(char *line, int len)
9309{
9310 char *p = line;
9311 int nleft = len;
9312 int c;
9313
9314 while (--nleft > 0) {
9315 c = pgetc2();
9316 if (c == PEOF) {
9317 if (p == line)
9318 return NULL;
9319 break;
9320 }
9321 *p++ = c;
9322 if (c == '\n')
9323 break;
9324 }
9325 *p = '\0';
9326 return line;
9327}
9328
Eric Andersenc470f442003-07-28 09:56:35 +00009329/*
9330 * Undo the last call to pgetc. Only one character may be pushed back.
9331 * PEOF may be pushed back.
9332 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009333static void
Eric Andersenc470f442003-07-28 09:56:35 +00009334pungetc(void)
9335{
9336 parsenleft++;
9337 parsenextc--;
9338}
Eric Andersencb57d552001-06-28 07:25:16 +00009339
9340/*
9341 * Push a string back onto the input at this current parsefile level.
9342 * We handle aliases this way.
9343 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009344#if !ENABLE_ASH_ALIAS
9345#define pushstring(s, ap) pushstring(s)
9346#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009347static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009348pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009349{
Eric Andersencb57d552001-06-28 07:25:16 +00009350 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009351 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009352
Eric Andersenc470f442003-07-28 09:56:35 +00009353 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009354 INT_OFF;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009355 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009356 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009357 sp->prev = g_parsefile->strpush;
9358 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009359 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009360 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009361 sp->prevstring = parsenextc;
9362 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009363#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009364 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009365 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009366 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009367 sp->string = s;
9368 }
Eric Andersen2870d962001-07-02 17:27:21 +00009369#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009370 parsenextc = s;
9371 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009372 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009373}
9374
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009375/*
9376 * To handle the "." command, a stack of input files is used. Pushfile
9377 * adds a new entry to the stack and popfile restores the previous level.
9378 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009379static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009380pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009381{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009382 struct parsefile *pf;
9383
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009384 g_parsefile->nleft = parsenleft;
9385 g_parsefile->lleft = parselleft;
9386 g_parsefile->nextc = parsenextc;
9387 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009388 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009389 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009390 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009391 /*pf->strpush = NULL; - ckzalloc did it */
9392 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009393 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009394}
9395
9396static void
9397popfile(void)
9398{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009399 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009400
Denis Vlasenkob012b102007-02-19 22:43:01 +00009401 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009402 if (pf->fd >= 0)
9403 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009404 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009405 while (pf->strpush)
9406 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009407 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009408 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009409 parsenleft = g_parsefile->nleft;
9410 parselleft = g_parsefile->lleft;
9411 parsenextc = g_parsefile->nextc;
9412 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009413 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009414}
9415
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009416/*
9417 * Return to top level.
9418 */
9419static void
9420popallfiles(void)
9421{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009422 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009423 popfile();
9424}
9425
9426/*
9427 * Close the file(s) that the shell is reading commands from. Called
9428 * after a fork is done.
9429 */
9430static void
9431closescript(void)
9432{
9433 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009434 if (g_parsefile->fd > 0) {
9435 close(g_parsefile->fd);
9436 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009437 }
9438}
9439
9440/*
9441 * Like setinputfile, but takes an open file descriptor. Call this with
9442 * interrupts off.
9443 */
9444static void
9445setinputfd(int fd, int push)
9446{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009447 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009448 if (push) {
9449 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009450 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009451 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009452 g_parsefile->fd = fd;
9453 if (g_parsefile->buf == NULL)
9454 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009455 parselleft = parsenleft = 0;
9456 plinno = 1;
9457}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009458
Eric Andersenc470f442003-07-28 09:56:35 +00009459/*
9460 * Set the input to take input from a file. If push is set, push the
9461 * old input onto the stack first.
9462 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009463static int
9464setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009465{
9466 int fd;
9467 int fd2;
9468
Denis Vlasenkob012b102007-02-19 22:43:01 +00009469 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009470 fd = open(fname, O_RDONLY);
9471 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009472 if (flags & INPUT_NOFILE_OK)
9473 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009474 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009475 }
Eric Andersenc470f442003-07-28 09:56:35 +00009476 if (fd < 10) {
9477 fd2 = copyfd(fd, 10);
9478 close(fd);
9479 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009480 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009481 fd = fd2;
9482 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009483 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009484 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009485 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009486 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009487}
9488
Eric Andersencb57d552001-06-28 07:25:16 +00009489/*
9490 * Like setinputfile, but takes input from a string.
9491 */
Eric Andersenc470f442003-07-28 09:56:35 +00009492static void
9493setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009494{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009495 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009496 pushfile();
9497 parsenextc = string;
9498 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009499 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009500 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009501 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009502}
9503
9504
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009505/* ============ mail.c
9506 *
9507 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009508 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009509
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009510#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009511
Eric Andersencb57d552001-06-28 07:25:16 +00009512#define MAXMBOXES 10
9513
Eric Andersenc470f442003-07-28 09:56:35 +00009514/* times of mailboxes */
9515static time_t mailtime[MAXMBOXES];
9516/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009517static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009518
Eric Andersencb57d552001-06-28 07:25:16 +00009519/*
Eric Andersenc470f442003-07-28 09:56:35 +00009520 * Print appropriate message(s) if mail has arrived.
9521 * If mail_var_path_changed is set,
9522 * then the value of MAIL has mail_var_path_changed,
9523 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009524 */
Eric Andersenc470f442003-07-28 09:56:35 +00009525static void
9526chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009527{
Eric Andersencb57d552001-06-28 07:25:16 +00009528 const char *mpath;
9529 char *p;
9530 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009531 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009532 struct stackmark smark;
9533 struct stat statb;
9534
Eric Andersencb57d552001-06-28 07:25:16 +00009535 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009536 mpath = mpathset() ? mpathval() : mailval();
9537 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009538 p = padvance(&mpath, nullstr);
9539 if (p == NULL)
9540 break;
9541 if (*p == '\0')
9542 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009543 for (q = p; *q; q++)
9544 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009545#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009546 if (q[-1] != '/')
9547 abort();
9548#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009549 q[-1] = '\0'; /* delete trailing '/' */
9550 if (stat(p, &statb) < 0) {
9551 *mtp = 0;
9552 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009553 }
Eric Andersenc470f442003-07-28 09:56:35 +00009554 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9555 fprintf(
9556 stderr, snlfmt,
9557 pathopt ? pathopt : "you have mail"
9558 );
9559 }
9560 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009561 }
Eric Andersenc470f442003-07-28 09:56:35 +00009562 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009563 popstackmark(&smark);
9564}
Eric Andersencb57d552001-06-28 07:25:16 +00009565
Eric Andersenc470f442003-07-28 09:56:35 +00009566static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009567changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009568{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009569 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009570}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009571
Denis Vlasenko131ae172007-02-18 13:00:19 +00009572#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009573
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009574
9575/* ============ ??? */
9576
Eric Andersencb57d552001-06-28 07:25:16 +00009577/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009578 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009579 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009580static void
9581setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009582{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009583 char **newparam;
9584 char **ap;
9585 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009586
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009587 for (nparam = 0; argv[nparam]; nparam++)
9588 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009589 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9590 while (*argv) {
9591 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009592 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009593 *ap = NULL;
9594 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009595 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009596 shellparam.nparam = nparam;
9597 shellparam.p = newparam;
9598#if ENABLE_ASH_GETOPTS
9599 shellparam.optind = 1;
9600 shellparam.optoff = -1;
9601#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009602}
9603
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009604/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009605 * Process shell options. The global variable argptr contains a pointer
9606 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009607 *
9608 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9609 * For a non-interactive shell, an error condition encountered
9610 * by a special built-in ... shall cause the shell to write a diagnostic message
9611 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009612 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009613 * ...
9614 * Utility syntax error (option or operand error) Shall exit
9615 * ...
9616 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9617 * we see that bash does not do that (set "finishes" with error code 1 instead,
9618 * and shell continues), and people rely on this behavior!
9619 * Testcase:
9620 * set -o barfoo 2>/dev/null
9621 * echo $?
9622 *
9623 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009624 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009625static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009626plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009627{
9628 int i;
9629
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009630 if (name) {
9631 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009632 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009633 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009634 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009635 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009636 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009637 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009638 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009639 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009640 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009641 if (val) {
9642 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9643 } else {
9644 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9645 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009646 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009647 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009648}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009649static void
9650setoption(int flag, int val)
9651{
9652 int i;
9653
9654 for (i = 0; i < NOPTS; i++) {
9655 if (optletters(i) == flag) {
9656 optlist[i] = val;
9657 return;
9658 }
9659 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009660 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009661 /* NOTREACHED */
9662}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009663static int
Eric Andersenc470f442003-07-28 09:56:35 +00009664options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009665{
9666 char *p;
9667 int val;
9668 int c;
9669
9670 if (cmdline)
9671 minusc = NULL;
9672 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009673 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009674 if (c != '-' && c != '+')
9675 break;
9676 argptr++;
9677 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009678 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009679 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009680 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009681 if (!cmdline) {
9682 /* "-" means turn off -x and -v */
9683 if (p[0] == '\0')
9684 xflag = vflag = 0;
9685 /* "--" means reset params */
9686 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009687 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009688 }
Eric Andersenc470f442003-07-28 09:56:35 +00009689 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009690 }
Eric Andersencb57d552001-06-28 07:25:16 +00009691 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009692 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009693 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009694 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009695 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009696 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009697 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009698 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009699 /* it already printed err message */
9700 return 1; /* error */
9701 }
Eric Andersencb57d552001-06-28 07:25:16 +00009702 if (*argptr)
9703 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009704 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9705 isloginsh = 1;
9706 /* bash does not accept +-login, we also won't */
9707 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009708 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009709 isloginsh = 1;
9710 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009711 } else {
9712 setoption(c, val);
9713 }
9714 }
9715 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009716 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009717}
9718
Eric Andersencb57d552001-06-28 07:25:16 +00009719/*
Eric Andersencb57d552001-06-28 07:25:16 +00009720 * The shift builtin command.
9721 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009722static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009723shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009724{
9725 int n;
9726 char **ap1, **ap2;
9727
9728 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009729 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009730 n = number(argv[1]);
9731 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009732 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009733 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009734 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009735 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009736 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009737 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009738 }
9739 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009740 while ((*ap2++ = *ap1++) != NULL)
9741 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009742#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009743 shellparam.optind = 1;
9744 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009745#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009746 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009747 return 0;
9748}
9749
Eric Andersencb57d552001-06-28 07:25:16 +00009750/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009751 * POSIX requires that 'set' (but not export or readonly) output the
9752 * variables in lexicographic order - by the locale's collating order (sigh).
9753 * Maybe we could keep them in an ordered balanced binary tree
9754 * instead of hashed lists.
9755 * For now just roll 'em through qsort for printing...
9756 */
9757static int
9758showvars(const char *sep_prefix, int on, int off)
9759{
9760 const char *sep;
9761 char **ep, **epend;
9762
9763 ep = listvars(on, off, &epend);
9764 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9765
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009766 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009767
9768 for (; ep < epend; ep++) {
9769 const char *p;
9770 const char *q;
9771
9772 p = strchrnul(*ep, '=');
9773 q = nullstr;
9774 if (*p)
9775 q = single_quote(++p);
9776 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9777 }
9778 return 0;
9779}
9780
9781/*
Eric Andersencb57d552001-06-28 07:25:16 +00009782 * The set command builtin.
9783 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009784static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009785setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009786{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009787 int retval;
9788
Denis Vlasenko68404f12008-03-17 09:00:54 +00009789 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009790 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009791 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009792 retval = 1;
9793 if (!options(0)) { /* if no parse error... */
9794 retval = 0;
9795 optschanged();
9796 if (*argptr != NULL) {
9797 setparam(argptr);
9798 }
Eric Andersencb57d552001-06-28 07:25:16 +00009799 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009800 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009801 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009802}
9803
Denis Vlasenko131ae172007-02-18 13:00:19 +00009804#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009805static void
9806change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009807{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009808 /* Galois LFSR parameter */
9809 /* Taps at 32 31 29 1: */
9810 enum { MASK = 0x8000000b };
9811 /* Another example - taps at 32 31 30 10: */
9812 /* MASK = 0x00400007 */
9813
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009814 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009815 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009816 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009817
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009818 /* LCG has period of 2^32 and alternating lowest bit */
9819 random_LCG = 1664525 * random_LCG + 1013904223;
9820 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9821 t = (random_galois_LFSR << 1);
9822 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9823 t ^= MASK;
9824 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009825 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009826 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009827 * for $RANDOM range. Combining with subtraction is
9828 * just for fun. + and ^ would work equally well. */
9829 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009830 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009831 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009832 vrandom.flags &= ~VNOFUNC;
9833 } else {
9834 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009835 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009836 }
Eric Andersenef02f822004-03-11 13:34:24 +00009837}
Eric Andersen16767e22004-03-16 05:14:10 +00009838#endif
9839
Denis Vlasenko131ae172007-02-18 13:00:19 +00009840#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009841static int
Eric Andersenc470f442003-07-28 09:56:35 +00009842getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009843{
9844 char *p, *q;
9845 char c = '?';
9846 int done = 0;
9847 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009848 char s[12];
9849 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009850
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009851 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009852 return 1;
9853 optnext = optfirst + *param_optind - 1;
9854
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009855 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009856 p = NULL;
9857 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009858 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009859 if (p == NULL || *p == '\0') {
9860 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009861 p = *optnext;
9862 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009863 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009864 p = NULL;
9865 done = 1;
9866 goto out;
9867 }
9868 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009869 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009870 goto atend;
9871 }
9872
9873 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009874 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009875 if (*q == '\0') {
9876 if (optstr[0] == ':') {
9877 s[0] = c;
9878 s[1] = '\0';
9879 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009880 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009881 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009882 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009883 }
9884 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009885 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009886 }
9887 if (*++q == ':')
9888 q++;
9889 }
9890
9891 if (*++q == ':') {
9892 if (*p == '\0' && (p = *optnext) == NULL) {
9893 if (optstr[0] == ':') {
9894 s[0] = c;
9895 s[1] = '\0';
9896 err |= setvarsafe("OPTARG", s, 0);
9897 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009898 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009899 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009900 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009901 c = '?';
9902 }
Eric Andersenc470f442003-07-28 09:56:35 +00009903 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009904 }
9905
9906 if (p == *optnext)
9907 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009908 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009909 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009910 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009911 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009912 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009913 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009914 *param_optind = optnext - optfirst + 1;
9915 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009916 err |= setvarsafe("OPTIND", s, VNOFUNC);
9917 s[0] = c;
9918 s[1] = '\0';
9919 err |= setvarsafe(optvar, s, 0);
9920 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009921 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009922 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009923 flush_stdout_stderr();
9924 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009925 }
9926 return done;
9927}
Eric Andersenc470f442003-07-28 09:56:35 +00009928
9929/*
9930 * The getopts builtin. Shellparam.optnext points to the next argument
9931 * to be processed. Shellparam.optptr points to the next character to
9932 * be processed in the current argument. If shellparam.optnext is NULL,
9933 * then it's the first time getopts has been called.
9934 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009935static int
Eric Andersenc470f442003-07-28 09:56:35 +00009936getoptscmd(int argc, char **argv)
9937{
9938 char **optbase;
9939
9940 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009941 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009942 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009943 optbase = shellparam.p;
9944 if (shellparam.optind > shellparam.nparam + 1) {
9945 shellparam.optind = 1;
9946 shellparam.optoff = -1;
9947 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009948 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009949 optbase = &argv[3];
9950 if (shellparam.optind > argc - 2) {
9951 shellparam.optind = 1;
9952 shellparam.optoff = -1;
9953 }
9954 }
9955
9956 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009957 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009958}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009959#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009960
Eric Andersencb57d552001-06-28 07:25:16 +00009961
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009962/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009963
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009964struct heredoc {
9965 struct heredoc *next; /* next here document in list */
9966 union node *here; /* redirection node */
9967 char *eofmark; /* string indicating end of input */
9968 smallint striptabs; /* if set, strip leading tabs */
9969};
9970
9971static smallint tokpushback; /* last token pushed back */
9972static smallint parsebackquote; /* nonzero if we are inside backquotes */
9973static smallint quoteflag; /* set if (part of) last token was quoted */
9974static token_id_t lasttoken; /* last token read (integer id Txxx) */
9975static struct heredoc *heredoclist; /* list of here documents to read */
9976static char *wordtext; /* text of last word returned by readtoken */
9977static struct nodelist *backquotelist;
9978static union node *redirnode;
9979static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009980/*
9981 * NEOF is returned by parsecmd when it encounters an end of file. It
9982 * must be distinct from NULL, so we use the address of a variable that
9983 * happens to be handy.
9984 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009985#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009986
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009987static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009988static void
9989raise_error_syntax(const char *msg)
9990{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009991 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009992 /* NOTREACHED */
9993}
9994
9995/*
9996 * Called when an unexpected token is read during the parse. The argument
9997 * is the token that is expected, or -1 if more than one type of token can
9998 * occur at this point.
9999 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010000static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010001static void
10002raise_error_unexpected_syntax(int token)
10003{
10004 char msg[64];
10005 int l;
10006
10007 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10008 if (token >= 0)
10009 sprintf(msg + l, " (expecting %s)", tokname(token));
10010 raise_error_syntax(msg);
10011 /* NOTREACHED */
10012}
Eric Andersencb57d552001-06-28 07:25:16 +000010013
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010014#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010015
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010016/* parsing is heavily cross-recursive, need these forward decls */
10017static union node *andor(void);
10018static union node *pipeline(void);
10019static union node *parse_command(void);
10020static void parseheredoc(void);
10021static char peektoken(void);
10022static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010023
Eric Andersenc470f442003-07-28 09:56:35 +000010024static union node *
10025list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010026{
10027 union node *n1, *n2, *n3;
10028 int tok;
10029
Eric Andersenc470f442003-07-28 09:56:35 +000010030 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10031 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010032 return NULL;
10033 n1 = NULL;
10034 for (;;) {
10035 n2 = andor();
10036 tok = readtoken();
10037 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010038 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010039 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010040 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010041 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010042 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010043 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010044 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010045 n2 = n3;
10046 }
10047 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010048 }
10049 }
10050 if (n1 == NULL) {
10051 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010052 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010053 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010054 n3->type = NSEMI;
10055 n3->nbinary.ch1 = n1;
10056 n3->nbinary.ch2 = n2;
10057 n1 = n3;
10058 }
10059 switch (tok) {
10060 case TBACKGND:
10061 case TSEMI:
10062 tok = readtoken();
10063 /* fall through */
10064 case TNL:
10065 if (tok == TNL) {
10066 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010067 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010068 return n1;
10069 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010070 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010071 }
Eric Andersenc470f442003-07-28 09:56:35 +000010072 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010073 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010074 return n1;
10075 break;
10076 case TEOF:
10077 if (heredoclist)
10078 parseheredoc();
10079 else
Eric Andersenc470f442003-07-28 09:56:35 +000010080 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010081 return n1;
10082 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010083 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010084 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010085 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010086 return n1;
10087 }
10088 }
10089}
10090
Eric Andersenc470f442003-07-28 09:56:35 +000010091static union node *
10092andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010093{
Eric Andersencb57d552001-06-28 07:25:16 +000010094 union node *n1, *n2, *n3;
10095 int t;
10096
Eric Andersencb57d552001-06-28 07:25:16 +000010097 n1 = pipeline();
10098 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010099 t = readtoken();
10100 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010101 t = NAND;
10102 } else if (t == TOR) {
10103 t = NOR;
10104 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010105 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010106 return n1;
10107 }
Eric Andersenc470f442003-07-28 09:56:35 +000010108 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010109 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010110 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010111 n3->type = t;
10112 n3->nbinary.ch1 = n1;
10113 n3->nbinary.ch2 = n2;
10114 n1 = n3;
10115 }
10116}
10117
Eric Andersenc470f442003-07-28 09:56:35 +000010118static union node *
10119pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010120{
Eric Andersencb57d552001-06-28 07:25:16 +000010121 union node *n1, *n2, *pipenode;
10122 struct nodelist *lp, *prev;
10123 int negate;
10124
10125 negate = 0;
10126 TRACE(("pipeline: entered\n"));
10127 if (readtoken() == TNOT) {
10128 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010129 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010130 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010131 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010132 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010133 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010134 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010135 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010136 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010137 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010138 pipenode->npipe.cmdlist = lp;
10139 lp->n = n1;
10140 do {
10141 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010142 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010143 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010144 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010145 prev->next = lp;
10146 } while (readtoken() == TPIPE);
10147 lp->next = NULL;
10148 n1 = pipenode;
10149 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010150 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010151 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010152 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010153 n2->type = NNOT;
10154 n2->nnot.com = n1;
10155 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010156 }
10157 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010158}
10159
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010160static union node *
10161makename(void)
10162{
10163 union node *n;
10164
Denis Vlasenko597906c2008-02-20 16:38:54 +000010165 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010166 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010167 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010168 n->narg.text = wordtext;
10169 n->narg.backquote = backquotelist;
10170 return n;
10171}
10172
10173static void
10174fixredir(union node *n, const char *text, int err)
10175{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010176 int fd;
10177
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010178 TRACE(("Fix redir %s %d\n", text, err));
10179 if (!err)
10180 n->ndup.vname = NULL;
10181
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010182 fd = bb_strtou(text, NULL, 10);
10183 if (!errno && fd >= 0)
10184 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010185 else if (LONE_DASH(text))
10186 n->ndup.dupfd = -1;
10187 else {
10188 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010189 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010190 n->ndup.vname = makename();
10191 }
10192}
10193
10194/*
10195 * Returns true if the text contains nothing to expand (no dollar signs
10196 * or backquotes).
10197 */
10198static int
10199noexpand(char *text)
10200{
10201 char *p;
10202 char c;
10203
10204 p = text;
10205 while ((c = *p++) != '\0') {
10206 if (c == CTLQUOTEMARK)
10207 continue;
10208 if (c == CTLESC)
10209 p++;
10210 else if (SIT(c, BASESYNTAX) == CCTL)
10211 return 0;
10212 }
10213 return 1;
10214}
10215
10216static void
10217parsefname(void)
10218{
10219 union node *n = redirnode;
10220
10221 if (readtoken() != TWORD)
10222 raise_error_unexpected_syntax(-1);
10223 if (n->type == NHERE) {
10224 struct heredoc *here = heredoc;
10225 struct heredoc *p;
10226 int i;
10227
10228 if (quoteflag == 0)
10229 n->type = NXHERE;
10230 TRACE(("Here document %d\n", n->type));
10231 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010232 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010233 rmescapes(wordtext);
10234 here->eofmark = wordtext;
10235 here->next = NULL;
10236 if (heredoclist == NULL)
10237 heredoclist = here;
10238 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010239 for (p = heredoclist; p->next; p = p->next)
10240 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010241 p->next = here;
10242 }
10243 } else if (n->type == NTOFD || n->type == NFROMFD) {
10244 fixredir(n, wordtext, 0);
10245 } else {
10246 n->nfile.fname = makename();
10247 }
10248}
Eric Andersencb57d552001-06-28 07:25:16 +000010249
Eric Andersenc470f442003-07-28 09:56:35 +000010250static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010251simplecmd(void)
10252{
10253 union node *args, **app;
10254 union node *n = NULL;
10255 union node *vars, **vpp;
10256 union node **rpp, *redir;
10257 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010258#if ENABLE_ASH_BASH_COMPAT
10259 smallint double_brackets_flag = 0;
10260#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010261
10262 args = NULL;
10263 app = &args;
10264 vars = NULL;
10265 vpp = &vars;
10266 redir = NULL;
10267 rpp = &redir;
10268
10269 savecheckkwd = CHKALIAS;
10270 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010271 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010272 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010273 t = readtoken();
10274 switch (t) {
10275#if ENABLE_ASH_BASH_COMPAT
10276 case TAND: /* "&&" */
10277 case TOR: /* "||" */
10278 if (!double_brackets_flag) {
10279 tokpushback = 1;
10280 goto out;
10281 }
10282 wordtext = (char *) (t == TAND ? "-a" : "-o");
10283#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010284 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010285 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010286 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010287 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010288 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010289#if ENABLE_ASH_BASH_COMPAT
10290 if (strcmp("[[", wordtext) == 0)
10291 double_brackets_flag = 1;
10292 else if (strcmp("]]", wordtext) == 0)
10293 double_brackets_flag = 0;
10294#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010295 n->narg.backquote = backquotelist;
10296 if (savecheckkwd && isassignment(wordtext)) {
10297 *vpp = n;
10298 vpp = &n->narg.next;
10299 } else {
10300 *app = n;
10301 app = &n->narg.next;
10302 savecheckkwd = 0;
10303 }
10304 break;
10305 case TREDIR:
10306 *rpp = n = redirnode;
10307 rpp = &n->nfile.next;
10308 parsefname(); /* read name of redirection file */
10309 break;
10310 case TLP:
10311 if (args && app == &args->narg.next
10312 && !vars && !redir
10313 ) {
10314 struct builtincmd *bcmd;
10315 const char *name;
10316
10317 /* We have a function */
10318 if (readtoken() != TRP)
10319 raise_error_unexpected_syntax(TRP);
10320 name = n->narg.text;
10321 if (!goodname(name)
10322 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10323 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010324 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010325 }
10326 n->type = NDEFUN;
10327 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10328 n->narg.next = parse_command();
10329 return n;
10330 }
10331 /* fall through */
10332 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010333 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010334 goto out;
10335 }
10336 }
10337 out:
10338 *app = NULL;
10339 *vpp = NULL;
10340 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010341 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010342 n->type = NCMD;
10343 n->ncmd.args = args;
10344 n->ncmd.assign = vars;
10345 n->ncmd.redirect = redir;
10346 return n;
10347}
10348
10349static union node *
10350parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010351{
Eric Andersencb57d552001-06-28 07:25:16 +000010352 union node *n1, *n2;
10353 union node *ap, **app;
10354 union node *cp, **cpp;
10355 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010356 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010357 int t;
10358
10359 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010360 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010361
Eric Andersencb57d552001-06-28 07:25:16 +000010362 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010363 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010364 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010365 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010366 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010367 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010368 n1->type = NIF;
10369 n1->nif.test = list(0);
10370 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010371 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010372 n1->nif.ifpart = list(0);
10373 n2 = n1;
10374 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010375 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010376 n2 = n2->nif.elsepart;
10377 n2->type = NIF;
10378 n2->nif.test = list(0);
10379 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010380 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010381 n2->nif.ifpart = list(0);
10382 }
10383 if (lasttoken == TELSE)
10384 n2->nif.elsepart = list(0);
10385 else {
10386 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010387 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010388 }
Eric Andersenc470f442003-07-28 09:56:35 +000010389 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010390 break;
10391 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010392 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010393 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010394 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010395 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010396 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010397 got = readtoken();
10398 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010399 TRACE(("expecting DO got %s %s\n", tokname(got),
10400 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010401 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010402 }
10403 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010404 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010405 break;
10406 }
10407 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010408 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010409 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010410 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010411 n1->type = NFOR;
10412 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010413 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010414 if (readtoken() == TIN) {
10415 app = &ap;
10416 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010417 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010418 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010419 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010420 n2->narg.text = wordtext;
10421 n2->narg.backquote = backquotelist;
10422 *app = n2;
10423 app = &n2->narg.next;
10424 }
10425 *app = NULL;
10426 n1->nfor.args = ap;
10427 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010428 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010429 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010430 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010431 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010432 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010433 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010434 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010435 n1->nfor.args = n2;
10436 /*
10437 * Newline or semicolon here is optional (but note
10438 * that the original Bourne shell only allowed NL).
10439 */
10440 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010441 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010442 }
Eric Andersenc470f442003-07-28 09:56:35 +000010443 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010444 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010445 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010446 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010447 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010448 break;
10449 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010450 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010451 n1->type = NCASE;
10452 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010453 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010454 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010455 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010456 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010457 n2->narg.text = wordtext;
10458 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010459 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010460 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010461 } while (readtoken() == TNL);
10462 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010463 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010464 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010465 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010466 checkkwd = CHKNL | CHKKWD;
10467 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010468 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010469 if (lasttoken == TLP)
10470 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010471 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010472 cp->type = NCLIST;
10473 app = &cp->nclist.pattern;
10474 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010475 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010476 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010477 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010478 ap->narg.text = wordtext;
10479 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010480 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010481 break;
10482 app = &ap->narg.next;
10483 readtoken();
10484 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010485 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010486 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010487 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010488 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010489
Eric Andersenc470f442003-07-28 09:56:35 +000010490 cpp = &cp->nclist.next;
10491
10492 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010493 t = readtoken();
10494 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010495 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010496 raise_error_unexpected_syntax(TENDCASE);
10497 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010498 }
Eric Andersenc470f442003-07-28 09:56:35 +000010499 }
Eric Andersencb57d552001-06-28 07:25:16 +000010500 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010501 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010502 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010503 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010504 n1->type = NSUBSHELL;
10505 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010506 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010507 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010508 break;
10509 case TBEGIN:
10510 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010511 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010512 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010513 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010514 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010515 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010516 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010517 }
10518
Eric Andersenc470f442003-07-28 09:56:35 +000010519 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010520 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010521
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010522 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010523 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010524 checkkwd = CHKKWD | CHKALIAS;
10525 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010526 while (readtoken() == TREDIR) {
10527 *rpp = n2 = redirnode;
10528 rpp = &n2->nfile.next;
10529 parsefname();
10530 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010531 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010532 *rpp = NULL;
10533 if (redir) {
10534 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010535 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010536 n2->type = NREDIR;
10537 n2->nredir.n = n1;
10538 n1 = n2;
10539 }
10540 n1->nredir.redirect = redir;
10541 }
Eric Andersencb57d552001-06-28 07:25:16 +000010542 return n1;
10543}
10544
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010545#if ENABLE_ASH_BASH_COMPAT
10546static int decode_dollar_squote(void)
10547{
10548 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10549 int c, cnt;
10550 char *p;
10551 char buf[4];
10552
10553 c = pgetc();
10554 p = strchr(C_escapes, c);
10555 if (p) {
10556 buf[0] = c;
10557 p = buf;
10558 cnt = 3;
10559 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10560 do {
10561 c = pgetc();
10562 *++p = c;
10563 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10564 pungetc();
10565 } else if (c == 'x') { /* \xHH */
10566 do {
10567 c = pgetc();
10568 *++p = c;
10569 } while (isxdigit(c) && --cnt);
10570 pungetc();
10571 if (cnt == 3) { /* \x but next char is "bad" */
10572 c = 'x';
10573 goto unrecognized;
10574 }
10575 } else { /* simple seq like \\ or \t */
10576 p++;
10577 }
10578 *p = '\0';
10579 p = buf;
10580 c = bb_process_escape_sequence((void*)&p);
10581 } else { /* unrecognized "\z": print both chars unless ' or " */
10582 if (c != '\'' && c != '"') {
10583 unrecognized:
10584 c |= 0x100; /* "please encode \, then me" */
10585 }
10586 }
10587 return c;
10588}
10589#endif
10590
Eric Andersencb57d552001-06-28 07:25:16 +000010591/*
10592 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10593 * is not NULL, read a here document. In the latter case, eofmark is the
10594 * word which marks the end of the document and striptabs is true if
10595 * leading tabs should be stripped from the document. The argument firstc
10596 * is the first character of the input token or document.
10597 *
10598 * Because C does not have internal subroutines, I have simulated them
10599 * using goto's to implement the subroutine linkage. The following macros
10600 * will run code that appears at the end of readtoken1.
10601 */
Eric Andersen2870d962001-07-02 17:27:21 +000010602#define CHECKEND() {goto checkend; checkend_return:;}
10603#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10604#define PARSESUB() {goto parsesub; parsesub_return:;}
10605#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10606#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10607#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010608static int
Eric Andersenc470f442003-07-28 09:56:35 +000010609readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010610{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010611 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010612 int c = firstc;
10613 char *out;
10614 int len;
10615 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010616 struct nodelist *bqlist;
10617 smallint quotef;
10618 smallint dblquote;
10619 smallint oldstyle;
10620 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010621#if ENABLE_ASH_EXPAND_PRMT
10622 smallint pssyntax; /* we are expanding a prompt string */
10623#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010624 int varnest; /* levels of variables expansion */
10625 int arinest; /* levels of arithmetic expansion */
10626 int parenlevel; /* levels of parens in arithmetic */
10627 int dqvarnest; /* levels of variables expansion within double quotes */
10628
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010629 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10630
Eric Andersencb57d552001-06-28 07:25:16 +000010631#if __GNUC__
10632 /* Avoid longjmp clobbering */
10633 (void) &out;
10634 (void) &quotef;
10635 (void) &dblquote;
10636 (void) &varnest;
10637 (void) &arinest;
10638 (void) &parenlevel;
10639 (void) &dqvarnest;
10640 (void) &oldstyle;
10641 (void) &prevsyntax;
10642 (void) &syntax;
10643#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010644 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010645 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010646 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010647 oldstyle = 0;
10648 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010649#if ENABLE_ASH_EXPAND_PRMT
10650 pssyntax = (syntax == PSSYNTAX);
10651 if (pssyntax)
10652 syntax = DQSYNTAX;
10653#endif
10654 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010655 varnest = 0;
10656 arinest = 0;
10657 parenlevel = 0;
10658 dqvarnest = 0;
10659
10660 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010661 loop:
10662 /* For each line, until end of word */
10663 {
Eric Andersenc470f442003-07-28 09:56:35 +000010664 CHECKEND(); /* set c to PEOF if at end of here document */
10665 for (;;) { /* until end of line or end of word */
10666 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010667 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010668 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010669 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010670 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010671 USTPUTC(c, out);
10672 plinno++;
10673 if (doprompt)
10674 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010675 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010676 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010677 case CWORD:
10678 USTPUTC(c, out);
10679 break;
10680 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010681 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010682 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010683#if ENABLE_ASH_BASH_COMPAT
10684 if (c == '\\' && bash_dollar_squote) {
10685 c = decode_dollar_squote();
10686 if (c & 0x100) {
10687 USTPUTC('\\', out);
10688 c = (unsigned char)c;
10689 }
10690 }
10691#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010692 USTPUTC(c, out);
10693 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010694 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010695 c = pgetc2();
10696 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010697 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010698 USTPUTC('\\', out);
10699 pungetc();
10700 } else if (c == '\n') {
10701 if (doprompt)
10702 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010703 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010704#if ENABLE_ASH_EXPAND_PRMT
10705 if (c == '$' && pssyntax) {
10706 USTPUTC(CTLESC, out);
10707 USTPUTC('\\', out);
10708 }
10709#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010710 if (dblquote && c != '\\'
10711 && c != '`' && c != '$'
10712 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010713 ) {
10714 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010715 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010716 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010717 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010718 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010719 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010720 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010721 }
10722 break;
10723 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010724 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010725 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010726 if (eofmark == NULL) {
10727 USTPUTC(CTLQUOTEMARK, out);
10728 }
Eric Andersencb57d552001-06-28 07:25:16 +000010729 break;
10730 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010731 syntax = DQSYNTAX;
10732 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010733 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010734 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010735 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010736 if (eofmark != NULL && arinest == 0
10737 && varnest == 0
10738 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010739 USTPUTC(c, out);
10740 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010741 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010742 syntax = BASESYNTAX;
10743 dblquote = 0;
10744 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010745 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010746 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010747 }
10748 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010749 case CVAR: /* '$' */
10750 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010751 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010752 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010753 if (varnest > 0) {
10754 varnest--;
10755 if (dqvarnest > 0) {
10756 dqvarnest--;
10757 }
10758 USTPUTC(CTLENDVAR, out);
10759 } else {
10760 USTPUTC(c, out);
10761 }
10762 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010763#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010764 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010765 parenlevel++;
10766 USTPUTC(c, out);
10767 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010768 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010769 if (parenlevel > 0) {
10770 USTPUTC(c, out);
10771 --parenlevel;
10772 } else {
10773 if (pgetc() == ')') {
10774 if (--arinest == 0) {
10775 USTPUTC(CTLENDARI, out);
10776 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010777 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010778 } else
10779 USTPUTC(')', out);
10780 } else {
10781 /*
10782 * unbalanced parens
10783 * (don't 2nd guess - no error)
10784 */
10785 pungetc();
10786 USTPUTC(')', out);
10787 }
10788 }
10789 break;
10790#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010791 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010792 PARSEBACKQOLD();
10793 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010794 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010795 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010796 case CIGN:
10797 break;
10798 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010799 if (varnest == 0) {
10800#if ENABLE_ASH_BASH_COMPAT
10801 if (c == '&') {
10802 if (pgetc() == '>')
10803 c = 0x100 + '>'; /* flag &> */
10804 pungetc();
10805 }
10806#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010807 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010808 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010809#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010810 if (c != PEOA)
10811#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010812 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010813
Eric Andersencb57d552001-06-28 07:25:16 +000010814 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010815 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010816 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010817 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010818 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010819#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010820 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010821 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010822#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010823 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010824 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010825 if (varnest != 0) {
10826 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010827 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010828 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010829 }
10830 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010831 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010832 out = stackblock();
10833 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010834 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10835 && quotef == 0
10836 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010837 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010838 PARSEREDIR(); /* passed as params: out, c */
10839 lasttoken = TREDIR;
10840 return lasttoken;
10841 }
10842 /* else: non-number X seen, interpret it
10843 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010844 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010845 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010846 }
10847 quoteflag = quotef;
10848 backquotelist = bqlist;
10849 grabstackblock(len);
10850 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010851 lasttoken = TWORD;
10852 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010853/* end of readtoken routine */
10854
Eric Andersencb57d552001-06-28 07:25:16 +000010855/*
10856 * Check to see whether we are at the end of the here document. When this
10857 * is called, c is set to the first character of the next input line. If
10858 * we are at the end of the here document, this routine sets the c to PEOF.
10859 */
Eric Andersenc470f442003-07-28 09:56:35 +000010860checkend: {
10861 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010862#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010863 if (c == PEOA) {
10864 c = pgetc2();
10865 }
10866#endif
10867 if (striptabs) {
10868 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010869 c = pgetc2();
10870 }
Eric Andersenc470f442003-07-28 09:56:35 +000010871 }
10872 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010873 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010874 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010875
Eric Andersenc470f442003-07-28 09:56:35 +000010876 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010877 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10878 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010879 if (*p == '\n' && *q == '\0') {
10880 c = PEOF;
10881 plinno++;
10882 needprompt = doprompt;
10883 } else {
10884 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010885 }
10886 }
10887 }
10888 }
Eric Andersenc470f442003-07-28 09:56:35 +000010889 goto checkend_return;
10890}
Eric Andersencb57d552001-06-28 07:25:16 +000010891
Eric Andersencb57d552001-06-28 07:25:16 +000010892/*
10893 * Parse a redirection operator. The variable "out" points to a string
10894 * specifying the fd to be redirected. The variable "c" contains the
10895 * first character of the redirection operator.
10896 */
Eric Andersenc470f442003-07-28 09:56:35 +000010897parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010898 /* out is already checked to be a valid number or "" */
10899 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010900 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010901
Denis Vlasenko597906c2008-02-20 16:38:54 +000010902 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010903 if (c == '>') {
10904 np->nfile.fd = 1;
10905 c = pgetc();
10906 if (c == '>')
10907 np->type = NAPPEND;
10908 else if (c == '|')
10909 np->type = NCLOBBER;
10910 else if (c == '&')
10911 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010912 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010913 else {
10914 np->type = NTO;
10915 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010916 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010917 }
10918#if ENABLE_ASH_BASH_COMPAT
10919 else if (c == 0x100 + '>') { /* this flags &> redirection */
10920 np->nfile.fd = 1;
10921 pgetc(); /* this is '>', no need to check */
10922 np->type = NTO2;
10923 }
10924#endif
10925 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010926 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010927 c = pgetc();
10928 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010929 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010930 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010931 np = stzalloc(sizeof(struct nhere));
10932 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010933 }
10934 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010935 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010936 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010937 c = pgetc();
10938 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010939 heredoc->striptabs = 1;
10940 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010941 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010942 pungetc();
10943 }
10944 break;
10945
10946 case '&':
10947 np->type = NFROMFD;
10948 break;
10949
10950 case '>':
10951 np->type = NFROMTO;
10952 break;
10953
10954 default:
10955 np->type = NFROM;
10956 pungetc();
10957 break;
10958 }
Eric Andersencb57d552001-06-28 07:25:16 +000010959 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010960 if (fd >= 0)
10961 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010962 redirnode = np;
10963 goto parseredir_return;
10964}
Eric Andersencb57d552001-06-28 07:25:16 +000010965
Eric Andersencb57d552001-06-28 07:25:16 +000010966/*
10967 * Parse a substitution. At this point, we have read the dollar sign
10968 * and nothing else.
10969 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010970
10971/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10972 * (assuming ascii char codes, as the original implementation did) */
10973#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010974 (((unsigned)(c) - 33 < 32) \
10975 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010976parsesub: {
10977 int subtype;
10978 int typeloc;
10979 int flags;
10980 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010981 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010982
Eric Andersenc470f442003-07-28 09:56:35 +000010983 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010984 if (c <= PEOA_OR_PEOF
10985 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010986 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010987#if ENABLE_ASH_BASH_COMPAT
10988 if (c == '\'')
10989 bash_dollar_squote = 1;
10990 else
10991#endif
10992 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010993 pungetc();
10994 } else if (c == '(') { /* $(command) or $((arith)) */
10995 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010996#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010997 PARSEARITH();
10998#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010999 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011000#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011001 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011002 pungetc();
11003 PARSEBACKQNEW();
11004 }
11005 } else {
11006 USTPUTC(CTLVAR, out);
11007 typeloc = out - (char *)stackblock();
11008 USTPUTC(VSNORMAL, out);
11009 subtype = VSNORMAL;
11010 if (c == '{') {
11011 c = pgetc();
11012 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011013 c = pgetc();
11014 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011015 c = '#';
11016 else
11017 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011018 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011019 subtype = 0;
11020 }
11021 if (c > PEOA_OR_PEOF && is_name(c)) {
11022 do {
11023 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011024 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011025 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011026 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011027 do {
11028 STPUTC(c, out);
11029 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011030 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011031 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011032 USTPUTC(c, out);
11033 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011034 } else {
11035 badsub:
11036 raise_error_syntax("bad substitution");
11037 }
Eric Andersencb57d552001-06-28 07:25:16 +000011038
Eric Andersenc470f442003-07-28 09:56:35 +000011039 STPUTC('=', out);
11040 flags = 0;
11041 if (subtype == 0) {
11042 switch (c) {
11043 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011044 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011045#if ENABLE_ASH_BASH_COMPAT
11046 if (c == ':' || c == '$' || isdigit(c)) {
11047 pungetc();
11048 subtype = VSSUBSTR;
11049 break;
11050 }
11051#endif
11052 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011053 /*FALLTHROUGH*/
11054 default:
11055 p = strchr(types, c);
11056 if (p == NULL)
11057 goto badsub;
11058 subtype = p - types + VSNORMAL;
11059 break;
11060 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011061 case '#': {
11062 int cc = c;
11063 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11064 c = pgetc();
11065 if (c == cc)
11066 subtype++;
11067 else
11068 pungetc();
11069 break;
11070 }
11071#if ENABLE_ASH_BASH_COMPAT
11072 case '/':
11073 subtype = VSREPLACE;
11074 c = pgetc();
11075 if (c == '/')
11076 subtype++; /* VSREPLACEALL */
11077 else
11078 pungetc();
11079 break;
11080#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011081 }
Eric Andersenc470f442003-07-28 09:56:35 +000011082 } else {
11083 pungetc();
11084 }
11085 if (dblquote || arinest)
11086 flags |= VSQUOTE;
11087 *((char *)stackblock() + typeloc) = subtype | flags;
11088 if (subtype != VSNORMAL) {
11089 varnest++;
11090 if (dblquote || arinest) {
11091 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011092 }
11093 }
11094 }
Eric Andersenc470f442003-07-28 09:56:35 +000011095 goto parsesub_return;
11096}
Eric Andersencb57d552001-06-28 07:25:16 +000011097
Eric Andersencb57d552001-06-28 07:25:16 +000011098/*
11099 * Called to parse command substitutions. Newstyle is set if the command
11100 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11101 * list of commands (passed by reference), and savelen is the number of
11102 * characters on the top of the stack which must be preserved.
11103 */
Eric Andersenc470f442003-07-28 09:56:35 +000011104parsebackq: {
11105 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011106 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011107 union node *n;
11108 char *volatile str;
11109 struct jmploc jmploc;
11110 struct jmploc *volatile savehandler;
11111 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011112 smallint saveprompt = 0;
11113
Eric Andersencb57d552001-06-28 07:25:16 +000011114#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011115 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011116#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011117 savepbq = parsebackquote;
11118 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011119 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011120 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011121 exception_handler = savehandler;
11122 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011123 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011124 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011125 str = NULL;
11126 savelen = out - (char *)stackblock();
11127 if (savelen > 0) {
11128 str = ckmalloc(savelen);
11129 memcpy(str, stackblock(), savelen);
11130 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011131 savehandler = exception_handler;
11132 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011133 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011134 if (oldstyle) {
11135 /* We must read until the closing backquote, giving special
11136 treatment to some slashes, and then push the string and
11137 reread it as input, interpreting it normally. */
11138 char *pout;
11139 int pc;
11140 size_t psavelen;
11141 char *pstr;
11142
11143
11144 STARTSTACKSTR(pout);
11145 for (;;) {
11146 if (needprompt) {
11147 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011148 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011149 pc = pgetc();
11150 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011151 case '`':
11152 goto done;
11153
11154 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011155 pc = pgetc();
11156 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000011157 plinno++;
11158 if (doprompt)
11159 setprompt(2);
11160 /*
11161 * If eating a newline, avoid putting
11162 * the newline into the new character
11163 * stream (via the STPUTC after the
11164 * switch).
11165 */
11166 continue;
11167 }
11168 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011169 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011170 STPUTC('\\', pout);
11171 if (pc > PEOA_OR_PEOF) {
11172 break;
11173 }
11174 /* fall through */
11175
11176 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011177#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011178 case PEOA:
11179#endif
11180 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011181 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011182
11183 case '\n':
11184 plinno++;
11185 needprompt = doprompt;
11186 break;
11187
11188 default:
11189 break;
11190 }
11191 STPUTC(pc, pout);
11192 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011193 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011194 STPUTC('\0', pout);
11195 psavelen = pout - (char *)stackblock();
11196 if (psavelen > 0) {
11197 pstr = grabstackstr(pout);
11198 setinputstring(pstr);
11199 }
11200 }
11201 nlpp = &bqlist;
11202 while (*nlpp)
11203 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011204 *nlpp = stzalloc(sizeof(**nlpp));
11205 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011206 parsebackquote = oldstyle;
11207
11208 if (oldstyle) {
11209 saveprompt = doprompt;
11210 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011211 }
11212
Eric Andersenc470f442003-07-28 09:56:35 +000011213 n = list(2);
11214
11215 if (oldstyle)
11216 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011217 else if (readtoken() != TRP)
11218 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011219
11220 (*nlpp)->n = n;
11221 if (oldstyle) {
11222 /*
11223 * Start reading from old file again, ignoring any pushed back
11224 * tokens left from the backquote parsing
11225 */
11226 popfile();
11227 tokpushback = 0;
11228 }
11229 while (stackblocksize() <= savelen)
11230 growstackblock();
11231 STARTSTACKSTR(out);
11232 if (str) {
11233 memcpy(out, str, savelen);
11234 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011235 INT_OFF;
11236 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011237 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011238 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011239 }
11240 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011241 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011242 if (arinest || dblquote)
11243 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11244 else
11245 USTPUTC(CTLBACKQ, out);
11246 if (oldstyle)
11247 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011248 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011249}
11250
Denis Vlasenko131ae172007-02-18 13:00:19 +000011251#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011252/*
11253 * Parse an arithmetic expansion (indicate start of one and set state)
11254 */
Eric Andersenc470f442003-07-28 09:56:35 +000011255parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011256 if (++arinest == 1) {
11257 prevsyntax = syntax;
11258 syntax = ARISYNTAX;
11259 USTPUTC(CTLARI, out);
11260 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011261 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011262 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011263 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011264 } else {
11265 /*
11266 * we collapse embedded arithmetic expansion to
11267 * parenthesis, which should be equivalent
11268 */
11269 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011270 }
Eric Andersenc470f442003-07-28 09:56:35 +000011271 goto parsearith_return;
11272}
11273#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011274
Eric Andersenc470f442003-07-28 09:56:35 +000011275} /* end of readtoken */
11276
Eric Andersencb57d552001-06-28 07:25:16 +000011277/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011278 * Read the next input token.
11279 * If the token is a word, we set backquotelist to the list of cmds in
11280 * backquotes. We set quoteflag to true if any part of the word was
11281 * quoted.
11282 * If the token is TREDIR, then we set redirnode to a structure containing
11283 * the redirection.
11284 * In all cases, the variable startlinno is set to the number of the line
11285 * on which the token starts.
11286 *
11287 * [Change comment: here documents and internal procedures]
11288 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11289 * word parsing code into a separate routine. In this case, readtoken
11290 * doesn't need to have any internal procedures, but parseword does.
11291 * We could also make parseoperator in essence the main routine, and
11292 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011293 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011294#define NEW_xxreadtoken
11295#ifdef NEW_xxreadtoken
11296/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011297static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011298 '\n', '(', ')', /* singles */
11299 '&', '|', ';', /* doubles */
11300 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011301};
Eric Andersencb57d552001-06-28 07:25:16 +000011302
Denis Vlasenko834dee72008-10-07 09:18:30 +000011303#define xxreadtoken_singles 3
11304#define xxreadtoken_doubles 3
11305
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011306static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011307 TNL, TLP, TRP, /* only single occurrence allowed */
11308 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11309 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011310 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011311};
11312
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011313static int
11314xxreadtoken(void)
11315{
11316 int c;
11317
11318 if (tokpushback) {
11319 tokpushback = 0;
11320 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011321 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011322 if (needprompt) {
11323 setprompt(2);
11324 }
11325 startlinno = plinno;
11326 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011327 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011328 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11329 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011330
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011331 if (c == '#') {
11332 while ((c = pgetc()) != '\n' && c != PEOF)
11333 continue;
11334 pungetc();
11335 } else if (c == '\\') {
11336 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011337 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011338 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011339 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011340 startlinno = ++plinno;
11341 if (doprompt)
11342 setprompt(2);
11343 } else {
11344 const char *p;
11345
11346 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11347 if (c != PEOF) {
11348 if (c == '\n') {
11349 plinno++;
11350 needprompt = doprompt;
11351 }
11352
11353 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011354 if (p == NULL)
11355 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011356
Denis Vlasenko834dee72008-10-07 09:18:30 +000011357 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11358 int cc = pgetc();
11359 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011360 p += xxreadtoken_doubles + 1;
11361 } else {
11362 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011363#if ENABLE_ASH_BASH_COMPAT
11364 if (c == '&' && cc == '>') /* &> */
11365 break; /* return readtoken1(...) */
11366#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011367 }
11368 }
11369 }
11370 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11371 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011372 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011373 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011374
11375 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011376}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011377#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011378#define RETURN(token) return lasttoken = token
11379static int
11380xxreadtoken(void)
11381{
11382 int c;
11383
11384 if (tokpushback) {
11385 tokpushback = 0;
11386 return lasttoken;
11387 }
11388 if (needprompt) {
11389 setprompt(2);
11390 }
11391 startlinno = plinno;
11392 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011393 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011394 switch (c) {
11395 case ' ': case '\t':
11396#if ENABLE_ASH_ALIAS
11397 case PEOA:
11398#endif
11399 continue;
11400 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011401 while ((c = pgetc()) != '\n' && c != PEOF)
11402 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011403 pungetc();
11404 continue;
11405 case '\\':
11406 if (pgetc() == '\n') {
11407 startlinno = ++plinno;
11408 if (doprompt)
11409 setprompt(2);
11410 continue;
11411 }
11412 pungetc();
11413 goto breakloop;
11414 case '\n':
11415 plinno++;
11416 needprompt = doprompt;
11417 RETURN(TNL);
11418 case PEOF:
11419 RETURN(TEOF);
11420 case '&':
11421 if (pgetc() == '&')
11422 RETURN(TAND);
11423 pungetc();
11424 RETURN(TBACKGND);
11425 case '|':
11426 if (pgetc() == '|')
11427 RETURN(TOR);
11428 pungetc();
11429 RETURN(TPIPE);
11430 case ';':
11431 if (pgetc() == ';')
11432 RETURN(TENDCASE);
11433 pungetc();
11434 RETURN(TSEMI);
11435 case '(':
11436 RETURN(TLP);
11437 case ')':
11438 RETURN(TRP);
11439 default:
11440 goto breakloop;
11441 }
11442 }
11443 breakloop:
11444 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11445#undef RETURN
11446}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011447#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011448
11449static int
11450readtoken(void)
11451{
11452 int t;
11453#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011454 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011455#endif
11456
11457#if ENABLE_ASH_ALIAS
11458 top:
11459#endif
11460
11461 t = xxreadtoken();
11462
11463 /*
11464 * eat newlines
11465 */
11466 if (checkkwd & CHKNL) {
11467 while (t == TNL) {
11468 parseheredoc();
11469 t = xxreadtoken();
11470 }
11471 }
11472
11473 if (t != TWORD || quoteflag) {
11474 goto out;
11475 }
11476
11477 /*
11478 * check for keywords
11479 */
11480 if (checkkwd & CHKKWD) {
11481 const char *const *pp;
11482
11483 pp = findkwd(wordtext);
11484 if (pp) {
11485 lasttoken = t = pp - tokname_array;
11486 TRACE(("keyword %s recognized\n", tokname(t)));
11487 goto out;
11488 }
11489 }
11490
11491 if (checkkwd & CHKALIAS) {
11492#if ENABLE_ASH_ALIAS
11493 struct alias *ap;
11494 ap = lookupalias(wordtext, 1);
11495 if (ap != NULL) {
11496 if (*ap->val) {
11497 pushstring(ap->val, ap);
11498 }
11499 goto top;
11500 }
11501#endif
11502 }
11503 out:
11504 checkkwd = 0;
11505#if DEBUG
11506 if (!alreadyseen)
11507 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11508 else
11509 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11510#endif
11511 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011512}
11513
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011514static char
11515peektoken(void)
11516{
11517 int t;
11518
11519 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011520 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011521 return tokname_array[t][0];
11522}
Eric Andersencb57d552001-06-28 07:25:16 +000011523
11524/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011525 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11526 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011527 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011528static union node *
11529parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011530{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011531 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011532
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011533 tokpushback = 0;
11534 doprompt = interact;
11535 if (doprompt)
11536 setprompt(doprompt);
11537 needprompt = 0;
11538 t = readtoken();
11539 if (t == TEOF)
11540 return NEOF;
11541 if (t == TNL)
11542 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011543 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011544 return list(1);
11545}
11546
11547/*
11548 * Input any here documents.
11549 */
11550static void
11551parseheredoc(void)
11552{
11553 struct heredoc *here;
11554 union node *n;
11555
11556 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011557 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011558
11559 while (here) {
11560 if (needprompt) {
11561 setprompt(2);
11562 }
11563 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11564 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011565 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011566 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011567 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011568 n->narg.text = wordtext;
11569 n->narg.backquote = backquotelist;
11570 here->here->nhere.doc = n;
11571 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011572 }
Eric Andersencb57d552001-06-28 07:25:16 +000011573}
11574
11575
11576/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011577 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011578 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011579#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011580static const char *
11581expandstr(const char *ps)
11582{
11583 union node n;
11584
11585 /* XXX Fix (char *) cast. */
11586 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011587 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011588 popfile();
11589
11590 n.narg.type = NARG;
11591 n.narg.next = NULL;
11592 n.narg.text = wordtext;
11593 n.narg.backquote = backquotelist;
11594
11595 expandarg(&n, NULL, 0);
11596 return stackblock();
11597}
11598#endif
11599
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011600/*
11601 * Execute a command or commands contained in a string.
11602 */
11603static int
11604evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011605{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011606 union node *n;
11607 struct stackmark smark;
11608 int skip;
11609
11610 setinputstring(s);
11611 setstackmark(&smark);
11612
11613 skip = 0;
11614 while ((n = parsecmd(0)) != NEOF) {
11615 evaltree(n, 0);
11616 popstackmark(&smark);
11617 skip = evalskip;
11618 if (skip)
11619 break;
11620 }
11621 popfile();
11622
11623 skip &= mask;
11624 evalskip = skip;
11625 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011626}
11627
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011628/*
11629 * The eval command.
11630 */
11631static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011632evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011633{
11634 char *p;
11635 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011636
Denis Vlasenko68404f12008-03-17 09:00:54 +000011637 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011638 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011639 argv += 2;
11640 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011641 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011642 for (;;) {
11643 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011644 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011645 if (p == NULL)
11646 break;
11647 STPUTC(' ', concat);
11648 }
11649 STPUTC('\0', concat);
11650 p = grabstackstr(concat);
11651 }
11652 evalstring(p, ~SKIPEVAL);
11653
11654 }
11655 return exitstatus;
11656}
11657
11658/*
11659 * Read and execute commands. "Top" is nonzero for the top level command
11660 * loop; it turns on prompting if the shell is interactive.
11661 */
11662static int
11663cmdloop(int top)
11664{
11665 union node *n;
11666 struct stackmark smark;
11667 int inter;
11668 int numeof = 0;
11669
11670 TRACE(("cmdloop(%d) called\n", top));
11671 for (;;) {
11672 int skip;
11673
11674 setstackmark(&smark);
11675#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011676 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011677 showjobs(stderr, SHOW_CHANGED);
11678#endif
11679 inter = 0;
11680 if (iflag && top) {
11681 inter++;
11682#if ENABLE_ASH_MAIL
11683 chkmail();
11684#endif
11685 }
11686 n = parsecmd(inter);
11687 /* showtree(n); DEBUG */
11688 if (n == NEOF) {
11689 if (!top || numeof >= 50)
11690 break;
11691 if (!stoppedjobs()) {
11692 if (!Iflag)
11693 break;
11694 out2str("\nUse \"exit\" to leave shell.\n");
11695 }
11696 numeof++;
11697 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011698 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11699 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011700 numeof = 0;
11701 evaltree(n, 0);
11702 }
11703 popstackmark(&smark);
11704 skip = evalskip;
11705
11706 if (skip) {
11707 evalskip = 0;
11708 return skip & SKIPEVAL;
11709 }
11710 }
11711 return 0;
11712}
11713
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011714/*
11715 * Take commands from a file. To be compatible we should do a path
11716 * search for the file, which is necessary to find sub-commands.
11717 */
11718static char *
11719find_dot_file(char *name)
11720{
11721 char *fullname;
11722 const char *path = pathval();
11723 struct stat statb;
11724
11725 /* don't try this for absolute or relative paths */
11726 if (strchr(name, '/'))
11727 return name;
11728
11729 while ((fullname = padvance(&path, name)) != NULL) {
11730 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11731 /*
11732 * Don't bother freeing here, since it will
11733 * be freed by the caller.
11734 */
11735 return fullname;
11736 }
11737 stunalloc(fullname);
11738 }
11739
11740 /* not found in the PATH */
11741 ash_msg_and_raise_error("%s: not found", name);
11742 /* NOTREACHED */
11743}
11744
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011745static int
11746dotcmd(int argc, char **argv)
11747{
11748 struct strlist *sp;
11749 volatile struct shparam saveparam;
11750 int status = 0;
11751
11752 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011753 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011754
Denis Vlasenko68404f12008-03-17 09:00:54 +000011755 if (argv[1]) { /* That's what SVR2 does */
11756 char *fullname = find_dot_file(argv[1]);
11757 argv += 2;
11758 argc -= 2;
11759 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011760 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011761 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011762 shellparam.nparam = argc;
11763 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011764 };
11765
11766 setinputfile(fullname, INPUT_PUSH_FILE);
11767 commandname = fullname;
11768 cmdloop(0);
11769 popfile();
11770
Denis Vlasenko68404f12008-03-17 09:00:54 +000011771 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011772 freeparam(&shellparam);
11773 shellparam = saveparam;
11774 };
11775 status = exitstatus;
11776 }
11777 return status;
11778}
11779
11780static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011781exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011782{
11783 if (stoppedjobs())
11784 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011785 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011786 exitstatus = number(argv[1]);
11787 raise_exception(EXEXIT);
11788 /* NOTREACHED */
11789}
11790
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011791/*
11792 * Read a file containing shell functions.
11793 */
11794static void
11795readcmdfile(char *name)
11796{
11797 setinputfile(name, INPUT_PUSH_FILE);
11798 cmdloop(0);
11799 popfile();
11800}
11801
11802
Denis Vlasenkocc571512007-02-23 21:10:35 +000011803/* ============ find_command inplementation */
11804
11805/*
11806 * Resolve a command name. If you change this routine, you may have to
11807 * change the shellexec routine as well.
11808 */
11809static void
11810find_command(char *name, struct cmdentry *entry, int act, const char *path)
11811{
11812 struct tblentry *cmdp;
11813 int idx;
11814 int prev;
11815 char *fullname;
11816 struct stat statb;
11817 int e;
11818 int updatetbl;
11819 struct builtincmd *bcmd;
11820
11821 /* If name contains a slash, don't use PATH or hash table */
11822 if (strchr(name, '/') != NULL) {
11823 entry->u.index = -1;
11824 if (act & DO_ABS) {
11825 while (stat(name, &statb) < 0) {
11826#ifdef SYSV
11827 if (errno == EINTR)
11828 continue;
11829#endif
11830 entry->cmdtype = CMDUNKNOWN;
11831 return;
11832 }
11833 }
11834 entry->cmdtype = CMDNORMAL;
11835 return;
11836 }
11837
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011838/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011839
11840 updatetbl = (path == pathval());
11841 if (!updatetbl) {
11842 act |= DO_ALTPATH;
11843 if (strstr(path, "%builtin") != NULL)
11844 act |= DO_ALTBLTIN;
11845 }
11846
11847 /* If name is in the table, check answer will be ok */
11848 cmdp = cmdlookup(name, 0);
11849 if (cmdp != NULL) {
11850 int bit;
11851
11852 switch (cmdp->cmdtype) {
11853 default:
11854#if DEBUG
11855 abort();
11856#endif
11857 case CMDNORMAL:
11858 bit = DO_ALTPATH;
11859 break;
11860 case CMDFUNCTION:
11861 bit = DO_NOFUNC;
11862 break;
11863 case CMDBUILTIN:
11864 bit = DO_ALTBLTIN;
11865 break;
11866 }
11867 if (act & bit) {
11868 updatetbl = 0;
11869 cmdp = NULL;
11870 } else if (cmdp->rehash == 0)
11871 /* if not invalidated by cd, we're done */
11872 goto success;
11873 }
11874
11875 /* If %builtin not in path, check for builtin next */
11876 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011877 if (bcmd) {
11878 if (IS_BUILTIN_REGULAR(bcmd))
11879 goto builtin_success;
11880 if (act & DO_ALTPATH) {
11881 if (!(act & DO_ALTBLTIN))
11882 goto builtin_success;
11883 } else if (builtinloc <= 0) {
11884 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011885 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011886 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011887
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011888#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011889 {
11890 int applet_no = find_applet_by_name(name);
11891 if (applet_no >= 0) {
11892 entry->cmdtype = CMDNORMAL;
11893 entry->u.index = -2 - applet_no;
11894 return;
11895 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011896 }
11897#endif
11898
Denis Vlasenkocc571512007-02-23 21:10:35 +000011899 /* We have to search path. */
11900 prev = -1; /* where to start */
11901 if (cmdp && cmdp->rehash) { /* doing a rehash */
11902 if (cmdp->cmdtype == CMDBUILTIN)
11903 prev = builtinloc;
11904 else
11905 prev = cmdp->param.index;
11906 }
11907
11908 e = ENOENT;
11909 idx = -1;
11910 loop:
11911 while ((fullname = padvance(&path, name)) != NULL) {
11912 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011913 /* NB: code below will still use fullname
11914 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011915 idx++;
11916 if (pathopt) {
11917 if (prefix(pathopt, "builtin")) {
11918 if (bcmd)
11919 goto builtin_success;
11920 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011921 }
11922 if ((act & DO_NOFUNC)
11923 || !prefix(pathopt, "func")
11924 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011925 continue;
11926 }
11927 }
11928 /* if rehash, don't redo absolute path names */
11929 if (fullname[0] == '/' && idx <= prev) {
11930 if (idx < prev)
11931 continue;
11932 TRACE(("searchexec \"%s\": no change\n", name));
11933 goto success;
11934 }
11935 while (stat(fullname, &statb) < 0) {
11936#ifdef SYSV
11937 if (errno == EINTR)
11938 continue;
11939#endif
11940 if (errno != ENOENT && errno != ENOTDIR)
11941 e = errno;
11942 goto loop;
11943 }
11944 e = EACCES; /* if we fail, this will be the error */
11945 if (!S_ISREG(statb.st_mode))
11946 continue;
11947 if (pathopt) { /* this is a %func directory */
11948 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011949 /* NB: stalloc will return space pointed by fullname
11950 * (because we don't have any intervening allocations
11951 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011952 readcmdfile(fullname);
11953 cmdp = cmdlookup(name, 0);
11954 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11955 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11956 stunalloc(fullname);
11957 goto success;
11958 }
11959 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11960 if (!updatetbl) {
11961 entry->cmdtype = CMDNORMAL;
11962 entry->u.index = idx;
11963 return;
11964 }
11965 INT_OFF;
11966 cmdp = cmdlookup(name, 1);
11967 cmdp->cmdtype = CMDNORMAL;
11968 cmdp->param.index = idx;
11969 INT_ON;
11970 goto success;
11971 }
11972
11973 /* We failed. If there was an entry for this command, delete it */
11974 if (cmdp && updatetbl)
11975 delete_cmd_entry();
11976 if (act & DO_ERR)
11977 ash_msg("%s: %s", name, errmsg(e, "not found"));
11978 entry->cmdtype = CMDUNKNOWN;
11979 return;
11980
11981 builtin_success:
11982 if (!updatetbl) {
11983 entry->cmdtype = CMDBUILTIN;
11984 entry->u.cmd = bcmd;
11985 return;
11986 }
11987 INT_OFF;
11988 cmdp = cmdlookup(name, 1);
11989 cmdp->cmdtype = CMDBUILTIN;
11990 cmdp->param.cmd = bcmd;
11991 INT_ON;
11992 success:
11993 cmdp->rehash = 0;
11994 entry->cmdtype = cmdp->cmdtype;
11995 entry->u = cmdp->param;
11996}
11997
11998
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011999/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012000
Eric Andersencb57d552001-06-28 07:25:16 +000012001/*
Eric Andersencb57d552001-06-28 07:25:16 +000012002 * The trap builtin.
12003 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012004static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012005trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012006{
12007 char *action;
12008 char **ap;
12009 int signo;
12010
Eric Andersenc470f442003-07-28 09:56:35 +000012011 nextopt(nullstr);
12012 ap = argptr;
12013 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012014 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012015 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012016 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012017 single_quote(trap[signo]),
12018 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012019 }
12020 }
12021 return 0;
12022 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012023 action = NULL;
12024 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012025 action = *ap++;
12026 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012027 signo = get_signum(*ap);
12028 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012029 ash_msg_and_raise_error("%s: bad trap", *ap);
12030 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012031 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012032 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012033 action = NULL;
12034 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012035 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012036 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012037 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012038 trap[signo] = action;
12039 if (signo != 0)
12040 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012041 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012042 ap++;
12043 }
12044 return 0;
12045}
12046
Eric Andersenc470f442003-07-28 09:56:35 +000012047
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012048/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012049
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012050#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012051/*
12052 * Lists available builtins
12053 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012054static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012055helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012056{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012057 unsigned col;
12058 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012059
12060 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012061 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012062 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012063 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012064 if (col > 60) {
12065 out1fmt("\n");
12066 col = 0;
12067 }
12068 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012069#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012070 {
12071 const char *a = applet_names;
12072 while (*a) {
12073 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12074 if (col > 60) {
12075 out1fmt("\n");
12076 col = 0;
12077 }
12078 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012079 }
12080 }
12081#endif
12082 out1fmt("\n\n");
12083 return EXIT_SUCCESS;
12084}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012085#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012086
Eric Andersencb57d552001-06-28 07:25:16 +000012087/*
Eric Andersencb57d552001-06-28 07:25:16 +000012088 * The export and readonly commands.
12089 */
Eric Andersenc470f442003-07-28 09:56:35 +000012090static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012091exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012092{
12093 struct var *vp;
12094 char *name;
12095 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012096 char **aptr;
12097 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012098
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012099 if (nextopt("p") != 'p') {
12100 aptr = argptr;
12101 name = *aptr;
12102 if (name) {
12103 do {
12104 p = strchr(name, '=');
12105 if (p != NULL) {
12106 p++;
12107 } else {
12108 vp = *findvar(hashvar(name), name);
12109 if (vp) {
12110 vp->flags |= flag;
12111 continue;
12112 }
Eric Andersencb57d552001-06-28 07:25:16 +000012113 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012114 setvar(name, p, flag);
12115 } while ((name = *++aptr) != NULL);
12116 return 0;
12117 }
Eric Andersencb57d552001-06-28 07:25:16 +000012118 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012119 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012120 return 0;
12121}
12122
Eric Andersencb57d552001-06-28 07:25:16 +000012123/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012124 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012125 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012126static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012127unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012128{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012129 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012130
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012131 cmdp = cmdlookup(name, 0);
12132 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12133 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012134}
12135
Eric Andersencb57d552001-06-28 07:25:16 +000012136/*
Eric Andersencb57d552001-06-28 07:25:16 +000012137 * The unset builtin command. We unset the function before we unset the
12138 * variable to allow a function to be unset when there is a readonly variable
12139 * with the same name.
12140 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012141static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012142unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012143{
12144 char **ap;
12145 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012146 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012147 int ret = 0;
12148
12149 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012150 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012151 }
Eric Andersencb57d552001-06-28 07:25:16 +000012152
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012153 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012154 if (flag != 'f') {
12155 i = unsetvar(*ap);
12156 ret |= i;
12157 if (!(i & 2))
12158 continue;
12159 }
12160 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012161 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012162 }
Eric Andersenc470f442003-07-28 09:56:35 +000012163 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012164}
12165
12166
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012167/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012168
Eric Andersenc470f442003-07-28 09:56:35 +000012169#include <sys/times.h>
12170
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012171static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012172 ' ', offsetof(struct tms, tms_utime),
12173 '\n', offsetof(struct tms, tms_stime),
12174 ' ', offsetof(struct tms, tms_cutime),
12175 '\n', offsetof(struct tms, tms_cstime),
12176 0
12177};
Eric Andersencb57d552001-06-28 07:25:16 +000012178
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012179static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012180timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012181{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012182 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012183 const unsigned char *p;
12184 struct tms buf;
12185
12186 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012187 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012188
12189 p = timescmd_str;
12190 do {
12191 t = *(clock_t *)(((char *) &buf) + p[1]);
12192 s = t / clk_tck;
12193 out1fmt("%ldm%ld.%.3lds%c",
12194 s/60, s%60,
12195 ((t - s * clk_tck) * 1000) / clk_tck,
12196 p[0]);
12197 } while (*(p += 2));
12198
Eric Andersencb57d552001-06-28 07:25:16 +000012199 return 0;
12200}
12201
Denis Vlasenko131ae172007-02-18 13:00:19 +000012202#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012203static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012204dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012205{
Eric Andersened9ecf72004-06-22 08:29:45 +000012206 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012207 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012208
Denis Vlasenkob012b102007-02-19 22:43:01 +000012209 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012210 result = arith(s, &errcode);
12211 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012212 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012213 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012214 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012215 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012216 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012217 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012218 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012219 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012220 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012221
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012222 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012223}
Eric Andersenc470f442003-07-28 09:56:35 +000012224
Eric Andersenc470f442003-07-28 09:56:35 +000012225/*
Eric Andersen90898442003-08-06 11:20:52 +000012226 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12227 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12228 *
12229 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012230 */
12231static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012232letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012233{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012234 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012235
Denis Vlasenko68404f12008-03-17 09:00:54 +000012236 argv++;
12237 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012238 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012239 do {
12240 i = dash_arith(*argv);
12241 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012242
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012243 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012244}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012245#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012246
Eric Andersenc470f442003-07-28 09:56:35 +000012247
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012248/* ============ miscbltin.c
12249 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012250 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012251 */
12252
12253#undef rflag
12254
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012255#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012256typedef enum __rlimit_resource rlim_t;
12257#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012258
Eric Andersenc470f442003-07-28 09:56:35 +000012259/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012260 * The read builtin. Options:
12261 * -r Do not interpret '\' specially
12262 * -s Turn off echo (tty only)
12263 * -n NCHARS Read NCHARS max
12264 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12265 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12266 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012267 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012268 * TODO: bash also has:
12269 * -a ARRAY Read into array[0],[1],etc
12270 * -d DELIM End on DELIM char, not newline
12271 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012272 */
Eric Andersenc470f442003-07-28 09:56:35 +000012273static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012274readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012275{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012276 static const char *const arg_REPLY[] = { "REPLY", NULL };
12277
Eric Andersenc470f442003-07-28 09:56:35 +000012278 char **ap;
12279 int backslash;
12280 char c;
12281 int rflag;
12282 char *prompt;
12283 const char *ifs;
12284 char *p;
12285 int startword;
12286 int status;
12287 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012288 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012289#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012290 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012291 int silent = 0;
12292 struct termios tty, old_tty;
12293#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012294#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012295 unsigned end_ms = 0;
12296 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012297#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012298
12299 rflag = 0;
12300 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012301 while ((i = nextopt("p:u:r"
12302 USE_ASH_READ_TIMEOUT("t:")
12303 USE_ASH_READ_NCHARS("n:s")
12304 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012305 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012306 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012307 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012308 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012309#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012310 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012311 nchars = bb_strtou(optionarg, NULL, 10);
12312 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012313 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012314 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012315 break;
12316 case 's':
12317 silent = 1;
12318 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012319#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012320#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012321 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012322 timeout = bb_strtou(optionarg, NULL, 10);
12323 if (errno || timeout > UINT_MAX / 2048)
12324 ash_msg_and_raise_error("invalid timeout");
12325 timeout *= 1000;
12326#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012327 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012328 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012329 /* EINVAL means number is ok, but not terminated by NUL */
12330 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012331 char *p2;
12332 if (*++p) {
12333 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012334 ts.tv_usec = bb_strtou(p, &p2, 10);
12335 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012336 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012337 scale = p2 - p;
12338 /* normalize to usec */
12339 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012340 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012341 while (scale++ < 6)
12342 ts.tv_usec *= 10;
12343 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012344 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012345 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012346 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012347 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012348 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012349 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012350#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012351 break;
12352#endif
12353 case 'r':
12354 rflag = 1;
12355 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012356 case 'u':
12357 fd = bb_strtou(optionarg, NULL, 10);
12358 if (fd < 0 || errno)
12359 ash_msg_and_raise_error("invalid file descriptor");
12360 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012361 default:
12362 break;
12363 }
Eric Andersenc470f442003-07-28 09:56:35 +000012364 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012365 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012366 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012367 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012368 ap = argptr;
12369 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012370 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012371 ifs = bltinlookup("IFS");
12372 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012373 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012374#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012375 tcgetattr(fd, &tty);
12376 old_tty = tty;
12377 if (nchars || silent) {
12378 if (nchars) {
12379 tty.c_lflag &= ~ICANON;
12380 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012381 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012382 if (silent) {
12383 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12384 }
12385 /* if tcgetattr failed, tcsetattr will fail too.
12386 * Ignoring, it's harmless. */
12387 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012388 }
12389#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012390
Eric Andersenc470f442003-07-28 09:56:35 +000012391 status = 0;
12392 startword = 1;
12393 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012394#if ENABLE_ASH_READ_TIMEOUT
12395 if (timeout) /* NB: ensuring end_ms is nonzero */
12396 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12397#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012398 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012399 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012400#if ENABLE_ASH_READ_TIMEOUT
12401 if (end_ms) {
12402 struct pollfd pfd[1];
12403 pfd[0].fd = fd;
12404 pfd[0].events = POLLIN;
12405 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12406 if ((int)timeout <= 0 /* already late? */
12407 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12408 ) { /* timed out! */
12409#if ENABLE_ASH_READ_NCHARS
12410 tcsetattr(fd, TCSANOW, &old_tty);
12411#endif
12412 return 1;
12413 }
12414 }
12415#endif
12416 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012417 status = 1;
12418 break;
12419 }
12420 if (c == '\0')
12421 continue;
12422 if (backslash) {
12423 backslash = 0;
12424 if (c != '\n')
12425 goto put;
12426 continue;
12427 }
12428 if (!rflag && c == '\\') {
12429 backslash++;
12430 continue;
12431 }
12432 if (c == '\n')
12433 break;
12434 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12435 continue;
12436 }
12437 startword = 0;
12438 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12439 STACKSTRNUL(p);
12440 setvar(*ap, stackblock(), 0);
12441 ap++;
12442 startword = 1;
12443 STARTSTACKSTR(p);
12444 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012445 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012446 STPUTC(c, p);
12447 }
12448 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012449/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012450#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012451 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012452#else
12453 while (1);
12454#endif
12455
12456#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012457 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012458#endif
12459
Eric Andersenc470f442003-07-28 09:56:35 +000012460 STACKSTRNUL(p);
12461 /* Remove trailing blanks */
12462 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12463 *p = '\0';
12464 setvar(*ap, stackblock(), 0);
12465 while (*++ap != NULL)
12466 setvar(*ap, nullstr, 0);
12467 return status;
12468}
12469
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012470static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012471umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012472{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012473 static const char permuser[3] ALIGN1 = "ugo";
12474 static const char permmode[3] ALIGN1 = "rwx";
12475 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012476 S_IRUSR, S_IWUSR, S_IXUSR,
12477 S_IRGRP, S_IWGRP, S_IXGRP,
12478 S_IROTH, S_IWOTH, S_IXOTH
12479 };
12480
12481 char *ap;
12482 mode_t mask;
12483 int i;
12484 int symbolic_mode = 0;
12485
12486 while (nextopt("S") != '\0') {
12487 symbolic_mode = 1;
12488 }
12489
Denis Vlasenkob012b102007-02-19 22:43:01 +000012490 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012491 mask = umask(0);
12492 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012493 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012494
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012495 ap = *argptr;
12496 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012497 if (symbolic_mode) {
12498 char buf[18];
12499 char *p = buf;
12500
12501 for (i = 0; i < 3; i++) {
12502 int j;
12503
12504 *p++ = permuser[i];
12505 *p++ = '=';
12506 for (j = 0; j < 3; j++) {
12507 if ((mask & permmask[3 * i + j]) == 0) {
12508 *p++ = permmode[j];
12509 }
12510 }
12511 *p++ = ',';
12512 }
12513 *--p = 0;
12514 puts(buf);
12515 } else {
12516 out1fmt("%.4o\n", mask);
12517 }
12518 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012519 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012520 mask = 0;
12521 do {
12522 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012523 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012524 mask = (mask << 3) + (*ap - '0');
12525 } while (*++ap != '\0');
12526 umask(mask);
12527 } else {
12528 mask = ~mask & 0777;
12529 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012530 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012531 }
12532 umask(~mask & 0777);
12533 }
12534 }
12535 return 0;
12536}
12537
12538/*
12539 * ulimit builtin
12540 *
12541 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12542 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12543 * ash by J.T. Conklin.
12544 *
12545 * Public domain.
12546 */
12547
12548struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012549 uint8_t cmd; /* RLIMIT_xxx fit into it */
12550 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012551 char option;
12552};
12553
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012554static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012555#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012556 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012557#endif
12558#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012559 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012560#endif
12561#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012562 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012563#endif
12564#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012565 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012566#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012567#ifdef RLIMIT_CORE
12568 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012569#endif
12570#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012571 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012572#endif
12573#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012574 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012575#endif
12576#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012577 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012578#endif
12579#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012580 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012581#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012582#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012583 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012584#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012585#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012586 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012587#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012588};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012589static const char limits_name[] =
12590#ifdef RLIMIT_CPU
12591 "time(seconds)" "\0"
12592#endif
12593#ifdef RLIMIT_FSIZE
12594 "file(blocks)" "\0"
12595#endif
12596#ifdef RLIMIT_DATA
12597 "data(kb)" "\0"
12598#endif
12599#ifdef RLIMIT_STACK
12600 "stack(kb)" "\0"
12601#endif
12602#ifdef RLIMIT_CORE
12603 "coredump(blocks)" "\0"
12604#endif
12605#ifdef RLIMIT_RSS
12606 "memory(kb)" "\0"
12607#endif
12608#ifdef RLIMIT_MEMLOCK
12609 "locked memory(kb)" "\0"
12610#endif
12611#ifdef RLIMIT_NPROC
12612 "process" "\0"
12613#endif
12614#ifdef RLIMIT_NOFILE
12615 "nofiles" "\0"
12616#endif
12617#ifdef RLIMIT_AS
12618 "vmemory(kb)" "\0"
12619#endif
12620#ifdef RLIMIT_LOCKS
12621 "locks" "\0"
12622#endif
12623;
Eric Andersenc470f442003-07-28 09:56:35 +000012624
Glenn L McGrath76620622004-01-13 10:19:37 +000012625enum limtype { SOFT = 0x1, HARD = 0x2 };
12626
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012627static void
12628printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012629 const struct limits *l)
12630{
12631 rlim_t val;
12632
12633 val = limit->rlim_max;
12634 if (how & SOFT)
12635 val = limit->rlim_cur;
12636
12637 if (val == RLIM_INFINITY)
12638 out1fmt("unlimited\n");
12639 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012640 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012641 out1fmt("%lld\n", (long long) val);
12642 }
12643}
12644
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012645static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012646ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012647{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012648 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012649 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012650 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012651 const struct limits *l;
12652 int set, all = 0;
12653 int optc, what;
12654 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012655
12656 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012657 while ((optc = nextopt("HSa"
12658#ifdef RLIMIT_CPU
12659 "t"
12660#endif
12661#ifdef RLIMIT_FSIZE
12662 "f"
12663#endif
12664#ifdef RLIMIT_DATA
12665 "d"
12666#endif
12667#ifdef RLIMIT_STACK
12668 "s"
12669#endif
12670#ifdef RLIMIT_CORE
12671 "c"
12672#endif
12673#ifdef RLIMIT_RSS
12674 "m"
12675#endif
12676#ifdef RLIMIT_MEMLOCK
12677 "l"
12678#endif
12679#ifdef RLIMIT_NPROC
12680 "p"
12681#endif
12682#ifdef RLIMIT_NOFILE
12683 "n"
12684#endif
12685#ifdef RLIMIT_AS
12686 "v"
12687#endif
12688#ifdef RLIMIT_LOCKS
12689 "w"
12690#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012691 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012692 switch (optc) {
12693 case 'H':
12694 how = HARD;
12695 break;
12696 case 'S':
12697 how = SOFT;
12698 break;
12699 case 'a':
12700 all = 1;
12701 break;
12702 default:
12703 what = optc;
12704 }
12705
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012706 for (l = limits_tbl; l->option != what; l++)
12707 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012708
12709 set = *argptr ? 1 : 0;
12710 if (set) {
12711 char *p = *argptr;
12712
12713 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012714 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012715 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012716 val = RLIM_INFINITY;
12717 else {
12718 val = (rlim_t) 0;
12719
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012720 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012721 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012722 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012723 if (val < (rlim_t) 0)
12724 break;
12725 }
12726 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012727 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012728 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012729 }
12730 }
12731 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012732 const char *lname = limits_name;
12733 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012734 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012735 out1fmt("%-20s ", lname);
12736 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012737 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012738 }
12739 return 0;
12740 }
12741
12742 getrlimit(l->cmd, &limit);
12743 if (set) {
12744 if (how & HARD)
12745 limit.rlim_max = val;
12746 if (how & SOFT)
12747 limit.rlim_cur = val;
12748 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012749 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012750 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012751 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012752 }
12753 return 0;
12754}
12755
Eric Andersen90898442003-08-06 11:20:52 +000012756
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012757/* ============ Math support */
12758
Denis Vlasenko131ae172007-02-18 13:00:19 +000012759#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012760
12761/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12762
12763 Permission is hereby granted, free of charge, to any person obtaining
12764 a copy of this software and associated documentation files (the
12765 "Software"), to deal in the Software without restriction, including
12766 without limitation the rights to use, copy, modify, merge, publish,
12767 distribute, sublicense, and/or sell copies of the Software, and to
12768 permit persons to whom the Software is furnished to do so, subject to
12769 the following conditions:
12770
12771 The above copyright notice and this permission notice shall be
12772 included in all copies or substantial portions of the Software.
12773
12774 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12775 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12776 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12777 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12778 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12779 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12780 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12781*/
12782
12783/* This is my infix parser/evaluator. It is optimized for size, intended
12784 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012785 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012786 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012787 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012788 * be that which POSIX specifies for shells. */
12789
12790/* The code uses a simple two-stack algorithm. See
12791 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012792 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012793 * this is based (this code differs in that it applies operators immediately
12794 * to the stack instead of adding them to a queue to end up with an
12795 * expression). */
12796
12797/* To use the routine, call it with an expression string and error return
12798 * pointer */
12799
12800/*
12801 * Aug 24, 2001 Manuel Novoa III
12802 *
12803 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12804 *
12805 * 1) In arith_apply():
12806 * a) Cached values of *numptr and &(numptr[-1]).
12807 * b) Removed redundant test for zero denominator.
12808 *
12809 * 2) In arith():
12810 * a) Eliminated redundant code for processing operator tokens by moving
12811 * to a table-based implementation. Also folded handling of parens
12812 * into the table.
12813 * b) Combined all 3 loops which called arith_apply to reduce generated
12814 * code size at the cost of speed.
12815 *
12816 * 3) The following expressions were treated as valid by the original code:
12817 * 1() , 0! , 1 ( *3 ) .
12818 * These bugs have been fixed by internally enclosing the expression in
12819 * parens and then checking that all binary ops and right parens are
12820 * preceded by a valid expression (NUM_TOKEN).
12821 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012822 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012823 * ctype's isspace() if it is used by another busybox applet or if additional
12824 * whitespace chars should be considered. Look below the "#include"s for a
12825 * precompiler test.
12826 */
12827
12828/*
12829 * Aug 26, 2001 Manuel Novoa III
12830 *
12831 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12832 *
12833 * Merge in Aaron's comments previously posted to the busybox list,
12834 * modified slightly to take account of my changes to the code.
12835 *
12836 */
12837
12838/*
12839 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12840 *
12841 * - allow access to variable,
12842 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12843 * - realize assign syntax (VAR=expr, +=, *= etc)
12844 * - realize exponentiation (** operator)
12845 * - realize comma separated - expr, expr
12846 * - realise ++expr --expr expr++ expr--
12847 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012848 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012849 * - was restored loses XOR operator
12850 * - remove one goto label, added three ;-)
12851 * - protect $((num num)) as true zero expr (Manuel`s error)
12852 * - always use special isspace(), see comment from bash ;-)
12853 */
12854
Eric Andersen90898442003-08-06 11:20:52 +000012855#define arith_isspace(arithval) \
12856 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12857
Eric Andersen90898442003-08-06 11:20:52 +000012858typedef unsigned char operator;
12859
12860/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012861 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012862 * precedence. The ID portion is so that multiple operators can have the
12863 * same precedence, ensuring that the leftmost one is evaluated first.
12864 * Consider * and /. */
12865
12866#define tok_decl(prec,id) (((id)<<5)|(prec))
12867#define PREC(op) ((op) & 0x1F)
12868
12869#define TOK_LPAREN tok_decl(0,0)
12870
12871#define TOK_COMMA tok_decl(1,0)
12872
12873#define TOK_ASSIGN tok_decl(2,0)
12874#define TOK_AND_ASSIGN tok_decl(2,1)
12875#define TOK_OR_ASSIGN tok_decl(2,2)
12876#define TOK_XOR_ASSIGN tok_decl(2,3)
12877#define TOK_PLUS_ASSIGN tok_decl(2,4)
12878#define TOK_MINUS_ASSIGN tok_decl(2,5)
12879#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12880#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12881
12882#define TOK_MUL_ASSIGN tok_decl(3,0)
12883#define TOK_DIV_ASSIGN tok_decl(3,1)
12884#define TOK_REM_ASSIGN tok_decl(3,2)
12885
12886/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012887#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012888
12889/* conditional is right associativity too */
12890#define TOK_CONDITIONAL tok_decl(4,0)
12891#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12892
12893#define TOK_OR tok_decl(5,0)
12894
12895#define TOK_AND tok_decl(6,0)
12896
12897#define TOK_BOR tok_decl(7,0)
12898
12899#define TOK_BXOR tok_decl(8,0)
12900
12901#define TOK_BAND tok_decl(9,0)
12902
12903#define TOK_EQ tok_decl(10,0)
12904#define TOK_NE tok_decl(10,1)
12905
12906#define TOK_LT tok_decl(11,0)
12907#define TOK_GT tok_decl(11,1)
12908#define TOK_GE tok_decl(11,2)
12909#define TOK_LE tok_decl(11,3)
12910
12911#define TOK_LSHIFT tok_decl(12,0)
12912#define TOK_RSHIFT tok_decl(12,1)
12913
12914#define TOK_ADD tok_decl(13,0)
12915#define TOK_SUB tok_decl(13,1)
12916
12917#define TOK_MUL tok_decl(14,0)
12918#define TOK_DIV tok_decl(14,1)
12919#define TOK_REM tok_decl(14,2)
12920
12921/* exponent is right associativity */
12922#define TOK_EXPONENT tok_decl(15,1)
12923
12924/* For now unary operators. */
12925#define UNARYPREC 16
12926#define TOK_BNOT tok_decl(UNARYPREC,0)
12927#define TOK_NOT tok_decl(UNARYPREC,1)
12928
12929#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12930#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12931
12932#define PREC_PRE (UNARYPREC+2)
12933
12934#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12935#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12936
12937#define PREC_POST (UNARYPREC+3)
12938
12939#define TOK_POST_INC tok_decl(PREC_POST, 0)
12940#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12941
12942#define SPEC_PREC (UNARYPREC+4)
12943
12944#define TOK_NUM tok_decl(SPEC_PREC, 0)
12945#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12946
12947#define NUMPTR (*numstackptr)
12948
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012949static int
12950tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012951{
12952 operator prec = PREC(op);
12953
12954 convert_prec_is_assing(prec);
12955 return (prec == PREC(TOK_ASSIGN) ||
12956 prec == PREC_PRE || prec == PREC_POST);
12957}
12958
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012959static int
12960is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012961{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012962 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12963 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012964}
12965
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012966typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012967 arith_t val;
12968 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012969 char contidional_second_val_initialized;
12970 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012971 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012972} v_n_t;
12973
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012974typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012975 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012976 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012977} chk_var_recursive_looped_t;
12978
12979static chk_var_recursive_looped_t *prev_chk_var_recursive;
12980
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012981static int
12982arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012983{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012984 if (t->var) {
12985 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012986
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012987 if (p) {
12988 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012989
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012990 /* recursive try as expression */
12991 chk_var_recursive_looped_t *cur;
12992 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012993
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012994 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12995 if (strcmp(cur->var, t->var) == 0) {
12996 /* expression recursion loop detected */
12997 return -5;
12998 }
12999 }
13000 /* save current lookuped var name */
13001 cur = prev_chk_var_recursive;
13002 cur_save.var = t->var;
13003 cur_save.next = cur;
13004 prev_chk_var_recursive = &cur_save;
13005
13006 t->val = arith (p, &errcode);
13007 /* restore previous ptr after recursiving */
13008 prev_chk_var_recursive = cur;
13009 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013010 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013011 /* allow undefined var as 0 */
13012 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013013 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013014 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013015}
13016
13017/* "applying" a token means performing it on the top elements on the integer
13018 * stack. For a unary operator it will only change the top element, but a
13019 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013020static int
13021arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013022{
Eric Andersen90898442003-08-06 11:20:52 +000013023 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013024 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013025 int ret_arith_lookup_val;
13026
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013027 /* There is no operator that can work without arguments */
13028 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013029 numptr_m1 = NUMPTR - 1;
13030
13031 /* check operand is var with noninteger value */
13032 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013033 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013034 return ret_arith_lookup_val;
13035
13036 rez = numptr_m1->val;
13037 if (op == TOK_UMINUS)
13038 rez *= -1;
13039 else if (op == TOK_NOT)
13040 rez = !rez;
13041 else if (op == TOK_BNOT)
13042 rez = ~rez;
13043 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13044 rez++;
13045 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13046 rez--;
13047 else if (op != TOK_UPLUS) {
13048 /* Binary operators */
13049
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013050 /* check and binary operators need two arguments */
13051 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013052
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013053 /* ... and they pop one */
13054 --NUMPTR;
13055 numptr_val = rez;
13056 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013057 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013058 /* protect $((expr1 ? expr2)) without ": expr" */
13059 goto err;
13060 }
13061 rez = numptr_m1->contidional_second_val;
13062 } else if (numptr_m1->contidional_second_val_initialized) {
13063 /* protect $((expr1 : expr2)) without "expr ? " */
13064 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013065 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013066 numptr_m1 = NUMPTR - 1;
13067 if (op != TOK_ASSIGN) {
13068 /* check operand is var with noninteger value for not '=' */
13069 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13070 if (ret_arith_lookup_val)
13071 return ret_arith_lookup_val;
13072 }
13073 if (op == TOK_CONDITIONAL) {
13074 numptr_m1->contidional_second_val = rez;
13075 }
13076 rez = numptr_m1->val;
13077 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013078 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013079 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013080 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013081 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013082 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013083 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013084 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013085 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013086 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013087 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013088 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013089 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013090 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013091 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013092 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013093 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013094 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013095 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013096 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013097 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013098 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013099 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013100 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013101 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013102 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013103 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013104 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013105 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013106 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013107 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013108 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013109 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013110 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013111 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013112 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013113 /* protect $((expr : expr)) without "expr ? " */
13114 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013115 }
13116 numptr_m1->contidional_second_val_initialized = op;
13117 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013118 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013119 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013120 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013121 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013122 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013123 return -3; /* exponent less than 0 */
13124 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013125 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013126
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013127 if (numptr_val)
13128 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013129 c *= rez;
13130 rez = c;
13131 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013132 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013133 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013134 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013135 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013136 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013137 rez %= numptr_val;
13138 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013139 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013140 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013141
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013142 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013143 /* Hmm, 1=2 ? */
13144 goto err;
13145 }
13146 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013147#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013148 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013149#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013150 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013151#endif
Eric Andersen90898442003-08-06 11:20:52 +000013152 setvar(numptr_m1->var, buf, 0);
13153 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013154 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013155 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013156 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013157 rez++;
13158 }
13159 numptr_m1->val = rez;
13160 /* protect geting var value, is number now */
13161 numptr_m1->var = NULL;
13162 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013163 err:
13164 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013165}
13166
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013167/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013168static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013169 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13170 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13171 '<','<', 0, TOK_LSHIFT,
13172 '>','>', 0, TOK_RSHIFT,
13173 '|','|', 0, TOK_OR,
13174 '&','&', 0, TOK_AND,
13175 '!','=', 0, TOK_NE,
13176 '<','=', 0, TOK_LE,
13177 '>','=', 0, TOK_GE,
13178 '=','=', 0, TOK_EQ,
13179 '|','=', 0, TOK_OR_ASSIGN,
13180 '&','=', 0, TOK_AND_ASSIGN,
13181 '*','=', 0, TOK_MUL_ASSIGN,
13182 '/','=', 0, TOK_DIV_ASSIGN,
13183 '%','=', 0, TOK_REM_ASSIGN,
13184 '+','=', 0, TOK_PLUS_ASSIGN,
13185 '-','=', 0, TOK_MINUS_ASSIGN,
13186 '-','-', 0, TOK_POST_DEC,
13187 '^','=', 0, TOK_XOR_ASSIGN,
13188 '+','+', 0, TOK_POST_INC,
13189 '*','*', 0, TOK_EXPONENT,
13190 '!', 0, TOK_NOT,
13191 '<', 0, TOK_LT,
13192 '>', 0, TOK_GT,
13193 '=', 0, TOK_ASSIGN,
13194 '|', 0, TOK_BOR,
13195 '&', 0, TOK_BAND,
13196 '*', 0, TOK_MUL,
13197 '/', 0, TOK_DIV,
13198 '%', 0, TOK_REM,
13199 '+', 0, TOK_ADD,
13200 '-', 0, TOK_SUB,
13201 '^', 0, TOK_BXOR,
13202 /* uniq */
13203 '~', 0, TOK_BNOT,
13204 ',', 0, TOK_COMMA,
13205 '?', 0, TOK_CONDITIONAL,
13206 ':', 0, TOK_CONDITIONAL_SEP,
13207 ')', 0, TOK_RPAREN,
13208 '(', 0, TOK_LPAREN,
13209 0
13210};
13211/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013212#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013213
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013214static arith_t
13215arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013216{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013217 char arithval; /* Current character under analysis */
13218 operator lasttok, op;
13219 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013220 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013221 const char *p = endexpression;
13222 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013223 v_n_t *numstack, *numstackptr;
13224 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013225
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013226 /* Stack of integers */
13227 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13228 * in any given correct or incorrect expression is left as an exercise to
13229 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013230 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013231 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013232 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013233
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013234 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13235 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013236
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013237 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013238 arithval = *expr;
13239 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013240 if (p == endexpression) {
13241 /* Null expression. */
13242 return 0;
13243 }
13244
13245 /* This is only reached after all tokens have been extracted from the
13246 * input stream. If there are still tokens on the operator stack, they
13247 * are to be applied in order. At the end, there should be a final
13248 * result on the integer stack */
13249
13250 if (expr != endexpression + 1) {
13251 /* If we haven't done so already, */
13252 /* append a closing right paren */
13253 expr = endexpression;
13254 /* and let the loop process it. */
13255 continue;
13256 }
13257 /* At this point, we're done with the expression. */
13258 if (numstackptr != numstack+1) {
13259 /* ... but if there isn't, it's bad */
13260 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013261 *perrcode = -1;
13262 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013263 }
13264 if (numstack->var) {
13265 /* expression is $((var)) only, lookup now */
13266 errcode = arith_lookup_val(numstack);
13267 }
13268 ret:
13269 *perrcode = errcode;
13270 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013271 }
13272
Eric Andersen90898442003-08-06 11:20:52 +000013273 /* Continue processing the expression. */
13274 if (arith_isspace(arithval)) {
13275 /* Skip whitespace */
13276 goto prologue;
13277 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013278 p = endofname(expr);
13279 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013280 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013281
13282 numstackptr->var = alloca(var_name_size);
13283 safe_strncpy(numstackptr->var, expr, var_name_size);
13284 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013285 num:
Eric Andersen90898442003-08-06 11:20:52 +000013286 numstackptr->contidional_second_val_initialized = 0;
13287 numstackptr++;
13288 lasttok = TOK_NUM;
13289 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013290 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013291 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013292 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013293#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013294 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013295#else
13296 numstackptr->val = strtol(expr, (char **) &expr, 0);
13297#endif
Eric Andersen90898442003-08-06 11:20:52 +000013298 goto num;
13299 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013300 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013301 const char *o;
13302
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013303 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013304 /* strange operator not found */
13305 goto err;
13306 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013307 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013308 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013309 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013310 /* found */
13311 expr = o - 1;
13312 break;
13313 }
13314 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013315 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013316 p++;
13317 /* skip zero delim */
13318 p++;
13319 }
13320 op = p[1];
13321
13322 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013323 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13324 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013325
13326 /* Plus and minus are binary (not unary) _only_ if the last
13327 * token was as number, or a right paren (which pretends to be
13328 * a number, since it evaluates to one). Think about it.
13329 * It makes sense. */
13330 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013331 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013332 case TOK_ADD:
13333 op = TOK_UPLUS;
13334 break;
13335 case TOK_SUB:
13336 op = TOK_UMINUS;
13337 break;
13338 case TOK_POST_INC:
13339 op = TOK_PRE_INC;
13340 break;
13341 case TOK_POST_DEC:
13342 op = TOK_PRE_DEC;
13343 break;
Eric Andersen90898442003-08-06 11:20:52 +000013344 }
13345 }
13346 /* We don't want a unary operator to cause recursive descent on the
13347 * stack, because there can be many in a row and it could cause an
13348 * operator to be evaluated before its argument is pushed onto the
13349 * integer stack. */
13350 /* But for binary operators, "apply" everything on the operator
13351 * stack until we find an operator with a lesser priority than the
13352 * one we have just extracted. */
13353 /* Left paren is given the lowest priority so it will never be
13354 * "applied" in this way.
13355 * if associativity is right and priority eq, applied also skip
13356 */
13357 prec = PREC(op);
13358 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13359 /* not left paren or unary */
13360 if (lasttok != TOK_NUM) {
13361 /* binary op must be preceded by a num */
13362 goto err;
13363 }
13364 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013365 if (op == TOK_RPAREN) {
13366 /* The algorithm employed here is simple: while we don't
13367 * hit an open paren nor the bottom of the stack, pop
13368 * tokens and apply them */
13369 if (stackptr[-1] == TOK_LPAREN) {
13370 --stackptr;
13371 /* Any operator directly after a */
13372 lasttok = TOK_NUM;
13373 /* close paren should consider itself binary */
13374 goto prologue;
13375 }
13376 } else {
13377 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013378
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013379 convert_prec_is_assing(prec);
13380 convert_prec_is_assing(prev_prec);
13381 if (prev_prec < prec)
13382 break;
13383 /* check right assoc */
13384 if (prev_prec == prec && is_right_associativity(prec))
13385 break;
13386 }
13387 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13388 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013389 }
13390 if (op == TOK_RPAREN) {
13391 goto err;
13392 }
13393 }
13394
13395 /* Push this operator to the stack and remember it. */
13396 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013397 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013398 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013399 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013400}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013401#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013402
13403
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013404/* ============ main() and helpers */
13405
13406/*
13407 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013408 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013409static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013410static void
13411exitshell(void)
13412{
13413 struct jmploc loc;
13414 char *p;
13415 int status;
13416
13417 status = exitstatus;
13418 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13419 if (setjmp(loc.loc)) {
13420 if (exception == EXEXIT)
13421/* dash bug: it just does _exit(exitstatus) here
13422 * but we have to do setjobctl(0) first!
13423 * (bug is still not fixed in dash-0.5.3 - if you run dash
13424 * under Midnight Commander, on exit from dash MC is backgrounded) */
13425 status = exitstatus;
13426 goto out;
13427 }
13428 exception_handler = &loc;
13429 p = trap[0];
13430 if (p) {
13431 trap[0] = NULL;
13432 evalstring(p, 0);
13433 }
13434 flush_stdout_stderr();
13435 out:
13436 setjobctl(0);
13437 _exit(status);
13438 /* NOTREACHED */
13439}
13440
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013441static void
13442init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013443{
13444 /* from input.c: */
13445 basepf.nextc = basepf.buf = basebuf;
13446
13447 /* from trap.c: */
13448 signal(SIGCHLD, SIG_DFL);
13449
13450 /* from var.c: */
13451 {
13452 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013453 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013454 const char *p;
13455 struct stat st1, st2;
13456
13457 initvar();
13458 for (envp = environ; envp && *envp; envp++) {
13459 if (strchr(*envp, '=')) {
13460 setvareq(*envp, VEXPORT|VTEXTFIXED);
13461 }
13462 }
13463
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013464 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013465 setvar("PPID", ppid, 0);
13466
13467 p = lookupvar("PWD");
13468 if (p)
13469 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13470 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13471 p = '\0';
13472 setpwd(p, 0);
13473 }
13474}
13475
13476/*
13477 * Process the shell command line arguments.
13478 */
13479static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013480procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013481{
13482 int i;
13483 const char *xminusc;
13484 char **xargv;
13485
13486 xargv = argv;
13487 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013488 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013489 xargv++;
13490 for (i = 0; i < NOPTS; i++)
13491 optlist[i] = 2;
13492 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013493 if (options(1)) {
13494 /* it already printed err message */
13495 raise_exception(EXERROR);
13496 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013497 xargv = argptr;
13498 xminusc = minusc;
13499 if (*xargv == NULL) {
13500 if (xminusc)
13501 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13502 sflag = 1;
13503 }
13504 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13505 iflag = 1;
13506 if (mflag == 2)
13507 mflag = iflag;
13508 for (i = 0; i < NOPTS; i++)
13509 if (optlist[i] == 2)
13510 optlist[i] = 0;
13511#if DEBUG == 2
13512 debug = 1;
13513#endif
13514 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13515 if (xminusc) {
13516 minusc = *xargv++;
13517 if (*xargv)
13518 goto setarg0;
13519 } else if (!sflag) {
13520 setinputfile(*xargv, 0);
13521 setarg0:
13522 arg0 = *xargv++;
13523 commandname = arg0;
13524 }
13525
13526 shellparam.p = xargv;
13527#if ENABLE_ASH_GETOPTS
13528 shellparam.optind = 1;
13529 shellparam.optoff = -1;
13530#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013531 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013532 while (*xargv) {
13533 shellparam.nparam++;
13534 xargv++;
13535 }
13536 optschanged();
13537}
13538
13539/*
13540 * Read /etc/profile or .profile.
13541 */
13542static void
13543read_profile(const char *name)
13544{
13545 int skip;
13546
13547 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13548 return;
13549 skip = cmdloop(0);
13550 popfile();
13551 if (skip)
13552 exitshell();
13553}
13554
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013555/*
13556 * This routine is called when an error or an interrupt occurs in an
13557 * interactive shell and control is returned to the main command loop.
13558 */
13559static void
13560reset(void)
13561{
13562 /* from eval.c: */
13563 evalskip = 0;
13564 loopnest = 0;
13565 /* from input.c: */
13566 parselleft = parsenleft = 0; /* clear input buffer */
13567 popallfiles();
13568 /* from parser.c: */
13569 tokpushback = 0;
13570 checkkwd = 0;
13571 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013572 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013573}
13574
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013575#if PROFILE
13576static short profile_buf[16384];
13577extern int etext();
13578#endif
13579
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013580/*
13581 * Main routine. We initialize things, parse the arguments, execute
13582 * profiles if we're a login shell, and then call cmdloop to execute
13583 * commands. The setjmp call sets up the location to jump to when an
13584 * exception occurs. When an exception occurs the variable "state"
13585 * is used to figure out how far we had gotten.
13586 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013587int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013588int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013589{
13590 char *shinit;
13591 volatile int state;
13592 struct jmploc jmploc;
13593 struct stackmark smark;
13594
Denis Vlasenko01631112007-12-16 17:20:38 +000013595 /* Initialize global data */
13596 INIT_G_misc();
13597 INIT_G_memstack();
13598 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013599#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013600 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013601#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013602 INIT_G_cmdtable();
13603
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013604#if PROFILE
13605 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13606#endif
13607
13608#if ENABLE_FEATURE_EDITING
13609 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13610#endif
13611 state = 0;
13612 if (setjmp(jmploc.loc)) {
13613 int e;
13614 int s;
13615
13616 reset();
13617
13618 e = exception;
13619 if (e == EXERROR)
13620 exitstatus = 2;
13621 s = state;
13622 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13623 exitshell();
13624
13625 if (e == EXINT) {
13626 outcslow('\n', stderr);
13627 }
13628 popstackmark(&smark);
13629 FORCE_INT_ON; /* enable interrupts */
13630 if (s == 1)
13631 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013632 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013633 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013634 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013635 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013636 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013637 }
13638 exception_handler = &jmploc;
13639#if DEBUG
13640 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013641 trace_puts("Shell args: ");
13642 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013643#endif
13644 rootpid = getpid();
13645
13646#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013647 /* Can use monotonic_ns() for better randomness but for now it is
13648 * not used anywhere else in busybox... so avoid bloat */
13649 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013650#endif
13651 init();
13652 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013653 procargs(argv);
13654
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013655#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13656 if (iflag) {
13657 const char *hp = lookupvar("HISTFILE");
13658
13659 if (hp == NULL) {
13660 hp = lookupvar("HOME");
13661 if (hp != NULL) {
13662 char *defhp = concat_path_file(hp, ".ash_history");
13663 setvar("HISTFILE", defhp, 0);
13664 free(defhp);
13665 }
13666 }
13667 }
13668#endif
13669 if (argv[0] && argv[0][0] == '-')
13670 isloginsh = 1;
13671 if (isloginsh) {
13672 state = 1;
13673 read_profile("/etc/profile");
13674 state1:
13675 state = 2;
13676 read_profile(".profile");
13677 }
13678 state2:
13679 state = 3;
13680 if (
13681#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013682 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013683#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013684 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013685 ) {
13686 shinit = lookupvar("ENV");
13687 if (shinit != NULL && *shinit != '\0') {
13688 read_profile(shinit);
13689 }
13690 }
13691 state3:
13692 state = 4;
13693 if (minusc)
13694 evalstring(minusc, 0);
13695
13696 if (sflag || minusc == NULL) {
13697#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013698 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013699 const char *hp = lookupvar("HISTFILE");
13700
13701 if (hp != NULL)
13702 line_input_state->hist_file = hp;
13703 }
13704#endif
13705 state4: /* XXX ??? - why isn't this before the "if" statement */
13706 cmdloop(1);
13707 }
13708#if PROFILE
13709 monitor(0);
13710#endif
13711#ifdef GPROF
13712 {
13713 extern void _mcleanup(void);
13714 _mcleanup();
13715 }
13716#endif
13717 exitshell();
13718 /* NOTREACHED */
13719}
13720
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013721#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013722const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013723int main(int argc, char **argv)
13724{
13725 return ash_main(argc, argv);
13726}
13727#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013728
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013729
Eric Andersendf82f612001-06-28 07:46:40 +000013730/*-
13731 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013732 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013733 *
13734 * This code is derived from software contributed to Berkeley by
13735 * Kenneth Almquist.
13736 *
13737 * Redistribution and use in source and binary forms, with or without
13738 * modification, are permitted provided that the following conditions
13739 * are met:
13740 * 1. Redistributions of source code must retain the above copyright
13741 * notice, this list of conditions and the following disclaimer.
13742 * 2. Redistributions in binary form must reproduce the above copyright
13743 * notice, this list of conditions and the following disclaimer in the
13744 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013745 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013746 * may be used to endorse or promote products derived from this software
13747 * without specific prior written permission.
13748 *
13749 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13750 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13751 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13752 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13753 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13754 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13755 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13756 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13757 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13758 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13759 * SUCH DAMAGE.
13760 */