blob: 9ccb1c9ee1473b173ae59e645654d46e5484bcd7 [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 Vlasenkobcceb0c2007-09-21 18:06:20 +00002575#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002576#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002577
Eric Andersenc470f442003-07-28 09:56:35 +00002578/* Syntax classes */
2579#define CWORD 0 /* character is nothing special */
2580#define CNL 1 /* newline character */
2581#define CBACK 2 /* a backslash character */
2582#define CSQUOTE 3 /* single quote */
2583#define CDQUOTE 4 /* double quote */
2584#define CENDQUOTE 5 /* a terminating quote */
2585#define CBQUOTE 6 /* backwards single quote */
2586#define CVAR 7 /* a dollar sign */
2587#define CENDVAR 8 /* a '}' character */
2588#define CLP 9 /* a left paren in arithmetic */
2589#define CRP 10 /* a right paren in arithmetic */
2590#define CENDFILE 11 /* end of file */
2591#define CCTL 12 /* like CWORD, except it must be escaped */
2592#define CSPCL 13 /* these terminate a word */
2593#define CIGN 14 /* character should be ignored */
2594
Denis Vlasenko131ae172007-02-18 13:00:19 +00002595#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002596#define SYNBASE 130
2597#define PEOF -130
2598#define PEOA -129
2599#define PEOA_OR_PEOF PEOA
2600#else
2601#define SYNBASE 129
2602#define PEOF -129
2603#define PEOA_OR_PEOF PEOF
2604#endif
2605
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002606/* number syntax index */
2607#define BASESYNTAX 0 /* not in quotes */
2608#define DQSYNTAX 1 /* in double quotes */
2609#define SQSYNTAX 2 /* in single quotes */
2610#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002611#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002612
Denis Vlasenko131ae172007-02-18 13:00:19 +00002613#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002614#define USE_SIT_FUNCTION
2615#endif
2616
Denis Vlasenko131ae172007-02-18 13:00:19 +00002617#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002618static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002619#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002620 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002621#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002622 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2623 { CNL, CNL, CNL, CNL }, /* 2, \n */
2624 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2625 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2626 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2627 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2628 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2629 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2630 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2631 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2632 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002633#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002634 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2635 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2636 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002637#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002638};
Eric Andersenc470f442003-07-28 09:56:35 +00002639#else
2640static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002641#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002642 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002643#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002644 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2645 { CNL, CNL, CNL }, /* 2, \n */
2646 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2647 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2648 { CVAR, CVAR, CWORD }, /* 5, $ */
2649 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2650 { CSPCL, CWORD, CWORD }, /* 7, ( */
2651 { CSPCL, CWORD, CWORD }, /* 8, ) */
2652 { CBACK, CBACK, CCTL }, /* 9, \ */
2653 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2654 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002655#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002656 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2657 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2658 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002659#endif
2660};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002661#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002662
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002663#ifdef USE_SIT_FUNCTION
2664
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002665static int
2666SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002667{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002668 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002669#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002670 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002671 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2672 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2673 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2674 11, 3 /* "}~" */
2675 };
2676#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002677 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002678 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2679 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2680 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2681 10, 2 /* "}~" */
2682 };
2683#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002684 const char *s;
2685 int indx;
2686
Eric Andersenc470f442003-07-28 09:56:35 +00002687 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002688 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002689#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002690 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002691 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002692 else
2693#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002694#define U_C(c) ((unsigned char)(c))
2695
2696 if ((unsigned char)c >= (unsigned char)(CTLESC)
2697 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2698 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002699 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002700 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002701 s = strchrnul(spec_symbls, c);
2702 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002703 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002704 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002705 }
2706 return S_I_T[indx][syntax];
2707}
2708
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002709#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002710
Denis Vlasenko131ae172007-02-18 13:00:19 +00002711#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002712#define CSPCL_CIGN_CIGN_CIGN 0
2713#define CSPCL_CWORD_CWORD_CWORD 1
2714#define CNL_CNL_CNL_CNL 2
2715#define CWORD_CCTL_CCTL_CWORD 3
2716#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2717#define CVAR_CVAR_CWORD_CVAR 5
2718#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2719#define CSPCL_CWORD_CWORD_CLP 7
2720#define CSPCL_CWORD_CWORD_CRP 8
2721#define CBACK_CBACK_CCTL_CBACK 9
2722#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2723#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2724#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2725#define CWORD_CWORD_CWORD_CWORD 13
2726#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002727#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002728#define CSPCL_CWORD_CWORD_CWORD 0
2729#define CNL_CNL_CNL_CNL 1
2730#define CWORD_CCTL_CCTL_CWORD 2
2731#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2732#define CVAR_CVAR_CWORD_CVAR 4
2733#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2734#define CSPCL_CWORD_CWORD_CLP 6
2735#define CSPCL_CWORD_CWORD_CRP 7
2736#define CBACK_CBACK_CCTL_CBACK 8
2737#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2738#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2739#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2740#define CWORD_CWORD_CWORD_CWORD 12
2741#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002742#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002743
2744static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002745 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002746 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002747#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002748 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2749#endif
2750 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2752 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2753 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2754 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2755 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2756 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2757 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2758 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002759 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2888 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2889 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2911 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002912 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002913 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2915 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002917 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002918 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2919 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2920 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2921 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2923 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2924 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2925 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2926 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2937 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2938 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2939 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2940 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2941 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2942 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2970 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2971 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2972 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2975 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3003 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3004 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3005 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003006};
3007
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003008#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3009
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003010#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003011
Eric Andersen2870d962001-07-02 17:27:21 +00003012
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003013/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003014
Denis Vlasenko131ae172007-02-18 13:00:19 +00003015#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003016
3017#define ALIASINUSE 1
3018#define ALIASDEAD 2
3019
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003020struct alias {
3021 struct alias *next;
3022 char *name;
3023 char *val;
3024 int flag;
3025};
3026
Denis Vlasenko01631112007-12-16 17:20:38 +00003027
3028static struct alias **atab; // [ATABSIZE];
3029#define INIT_G_alias() do { \
3030 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3031} while (0)
3032
Eric Andersen2870d962001-07-02 17:27:21 +00003033
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003034static struct alias **
3035__lookupalias(const char *name) {
3036 unsigned int hashval;
3037 struct alias **app;
3038 const char *p;
3039 unsigned int ch;
3040
3041 p = name;
3042
3043 ch = (unsigned char)*p;
3044 hashval = ch << 4;
3045 while (ch) {
3046 hashval += ch;
3047 ch = (unsigned char)*++p;
3048 }
3049 app = &atab[hashval % ATABSIZE];
3050
3051 for (; *app; app = &(*app)->next) {
3052 if (strcmp(name, (*app)->name) == 0) {
3053 break;
3054 }
3055 }
3056
3057 return app;
3058}
3059
3060static struct alias *
3061lookupalias(const char *name, int check)
3062{
3063 struct alias *ap = *__lookupalias(name);
3064
3065 if (check && ap && (ap->flag & ALIASINUSE))
3066 return NULL;
3067 return ap;
3068}
3069
3070static struct alias *
3071freealias(struct alias *ap)
3072{
3073 struct alias *next;
3074
3075 if (ap->flag & ALIASINUSE) {
3076 ap->flag |= ALIASDEAD;
3077 return ap;
3078 }
3079
3080 next = ap->next;
3081 free(ap->name);
3082 free(ap->val);
3083 free(ap);
3084 return next;
3085}
Eric Andersencb57d552001-06-28 07:25:16 +00003086
Eric Andersenc470f442003-07-28 09:56:35 +00003087static void
3088setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003089{
3090 struct alias *ap, **app;
3091
3092 app = __lookupalias(name);
3093 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003094 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003095 if (ap) {
3096 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003097 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003098 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003099 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003100 ap->flag &= ~ALIASDEAD;
3101 } else {
3102 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003103 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003104 ap->name = ckstrdup(name);
3105 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003106 /*ap->flag = 0; - ckzalloc did it */
3107 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003108 *app = ap;
3109 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003110 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003111}
3112
Eric Andersenc470f442003-07-28 09:56:35 +00003113static int
3114unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003115{
Eric Andersencb57d552001-06-28 07:25:16 +00003116 struct alias **app;
3117
3118 app = __lookupalias(name);
3119
3120 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003121 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003122 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003123 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003124 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003125 }
3126
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003127 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003128}
3129
Eric Andersenc470f442003-07-28 09:56:35 +00003130static void
3131rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003132{
Eric Andersencb57d552001-06-28 07:25:16 +00003133 struct alias *ap, **app;
3134 int i;
3135
Denis Vlasenkob012b102007-02-19 22:43:01 +00003136 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003137 for (i = 0; i < ATABSIZE; i++) {
3138 app = &atab[i];
3139 for (ap = *app; ap; ap = *app) {
3140 *app = freealias(*app);
3141 if (ap == *app) {
3142 app = &ap->next;
3143 }
3144 }
3145 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003146 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003147}
3148
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003149static void
3150printalias(const struct alias *ap)
3151{
3152 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3153}
3154
Eric Andersencb57d552001-06-28 07:25:16 +00003155/*
3156 * TODO - sort output
3157 */
Eric Andersenc470f442003-07-28 09:56:35 +00003158static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003159aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003160{
3161 char *n, *v;
3162 int ret = 0;
3163 struct alias *ap;
3164
Denis Vlasenko68404f12008-03-17 09:00:54 +00003165 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003166 int i;
3167
Denis Vlasenko68404f12008-03-17 09:00:54 +00003168 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003169 for (ap = atab[i]; ap; ap = ap->next) {
3170 printalias(ap);
3171 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003172 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003173 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003174 }
3175 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003176 v = strchr(n+1, '=');
3177 if (v == NULL) { /* n+1: funny ksh stuff */
3178 ap = *__lookupalias(n);
3179 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003180 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003181 ret = 1;
3182 } else
3183 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003184 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003185 *v++ = '\0';
3186 setalias(n, v);
3187 }
3188 }
3189
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003190 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003191}
3192
Eric Andersenc470f442003-07-28 09:56:35 +00003193static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003194unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003195{
3196 int i;
3197
3198 while ((i = nextopt("a")) != '\0') {
3199 if (i == 'a') {
3200 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003201 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003202 }
3203 }
3204 for (i = 0; *argptr; argptr++) {
3205 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003206 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003207 i = 1;
3208 }
3209 }
3210
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003211 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003212}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003213
Denis Vlasenko131ae172007-02-18 13:00:19 +00003214#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003215
Eric Andersenc470f442003-07-28 09:56:35 +00003216
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003217/* ============ jobs.c */
3218
3219/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3220#define FORK_FG 0
3221#define FORK_BG 1
3222#define FORK_NOJOB 2
3223
3224/* mode flags for showjob(s) */
3225#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3226#define SHOW_PID 0x04 /* include process pid */
3227#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3228
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003229/*
3230 * A job structure contains information about a job. A job is either a
3231 * single process or a set of processes contained in a pipeline. In the
3232 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3233 * array of pids.
3234 */
3235
3236struct procstat {
3237 pid_t pid; /* process id */
3238 int status; /* last process status from wait() */
3239 char *cmd; /* text of command being run */
3240};
3241
3242struct job {
3243 struct procstat ps0; /* status of process */
3244 struct procstat *ps; /* status or processes when more than one */
3245#if JOBS
3246 int stopstatus; /* status of a stopped job */
3247#endif
3248 uint32_t
3249 nprocs: 16, /* number of processes */
3250 state: 8,
3251#define JOBRUNNING 0 /* at least one proc running */
3252#define JOBSTOPPED 1 /* all procs are stopped */
3253#define JOBDONE 2 /* all procs are completed */
3254#if JOBS
3255 sigint: 1, /* job was killed by SIGINT */
3256 jobctl: 1, /* job running under job control */
3257#endif
3258 waited: 1, /* true if this entry has been waited for */
3259 used: 1, /* true if this entry is in used */
3260 changed: 1; /* true if status has changed */
3261 struct job *prev_job; /* previous job */
3262};
3263
Denis Vlasenko68404f12008-03-17 09:00:54 +00003264static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003265#if !JOBS
3266#define forkshell(job, node, mode) forkshell(job, mode)
3267#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003268static int forkshell(struct job *, union node *, int);
3269static int waitforjob(struct job *);
3270
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003271#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003272enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003273#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003274#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003275static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003276static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003277#endif
3278
3279/*
3280 * Set the signal handler for the specified signal. The routine figures
3281 * out what it should be set to.
3282 */
3283static void
3284setsignal(int signo)
3285{
3286 int action;
3287 char *t, tsig;
3288 struct sigaction act;
3289
3290 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003291 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003292 if (t == NULL)
3293 action = S_DFL;
3294 else if (*t != '\0')
3295 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003296 if (rootshell && action == S_DFL) {
3297 switch (signo) {
3298 case SIGINT:
3299 if (iflag || minusc || sflag == 0)
3300 action = S_CATCH;
3301 break;
3302 case SIGQUIT:
3303#if DEBUG
3304 if (debug)
3305 break;
3306#endif
3307 /* FALLTHROUGH */
3308 case SIGTERM:
3309 if (iflag)
3310 action = S_IGN;
3311 break;
3312#if JOBS
3313 case SIGTSTP:
3314 case SIGTTOU:
3315 if (mflag)
3316 action = S_IGN;
3317 break;
3318#endif
3319 }
3320 }
3321
3322 t = &sigmode[signo - 1];
3323 tsig = *t;
3324 if (tsig == 0) {
3325 /*
3326 * current setting unknown
3327 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003328 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329 /*
3330 * Pretend it worked; maybe we should give a warning
3331 * here, but other shells don't. We don't alter
3332 * sigmode, so that we retry every time.
3333 */
3334 return;
3335 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003336 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003337 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003338 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003339 if (mflag
3340 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3341 ) {
3342 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003343 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003344 }
3345 }
3346 if (tsig == S_HARD_IGN || tsig == action)
3347 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003348 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003349 switch (action) {
3350 case S_CATCH:
3351 act.sa_handler = onsig;
3352 break;
3353 case S_IGN:
3354 act.sa_handler = SIG_IGN;
3355 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356 }
3357 *t = action;
3358 act.sa_flags = 0;
3359 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003360 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003361}
3362
3363/* mode flags for set_curjob */
3364#define CUR_DELETE 2
3365#define CUR_RUNNING 1
3366#define CUR_STOPPED 0
3367
3368/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003369#define DOWAIT_NONBLOCK WNOHANG
3370#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003371
3372#if JOBS
3373/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003374static int initialpgrp; //references:2
3375static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003376#endif
3377/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003378static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003379/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003380static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003381/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003382static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003383/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003384static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003385
3386static void
3387set_curjob(struct job *jp, unsigned mode)
3388{
3389 struct job *jp1;
3390 struct job **jpp, **curp;
3391
3392 /* first remove from list */
3393 jpp = curp = &curjob;
3394 do {
3395 jp1 = *jpp;
3396 if (jp1 == jp)
3397 break;
3398 jpp = &jp1->prev_job;
3399 } while (1);
3400 *jpp = jp1->prev_job;
3401
3402 /* Then re-insert in correct position */
3403 jpp = curp;
3404 switch (mode) {
3405 default:
3406#if DEBUG
3407 abort();
3408#endif
3409 case CUR_DELETE:
3410 /* job being deleted */
3411 break;
3412 case CUR_RUNNING:
3413 /* newly created job or backgrounded job,
3414 put after all stopped jobs. */
3415 do {
3416 jp1 = *jpp;
3417#if JOBS
3418 if (!jp1 || jp1->state != JOBSTOPPED)
3419#endif
3420 break;
3421 jpp = &jp1->prev_job;
3422 } while (1);
3423 /* FALLTHROUGH */
3424#if JOBS
3425 case CUR_STOPPED:
3426#endif
3427 /* newly stopped job - becomes curjob */
3428 jp->prev_job = *jpp;
3429 *jpp = jp;
3430 break;
3431 }
3432}
3433
3434#if JOBS || DEBUG
3435static int
3436jobno(const struct job *jp)
3437{
3438 return jp - jobtab + 1;
3439}
3440#endif
3441
3442/*
3443 * Convert a job name to a job structure.
3444 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003445#if !JOBS
3446#define getjob(name, getctl) getjob(name)
3447#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003448static struct job *
3449getjob(const char *name, int getctl)
3450{
3451 struct job *jp;
3452 struct job *found;
3453 const char *err_msg = "No such job: %s";
3454 unsigned num;
3455 int c;
3456 const char *p;
3457 char *(*match)(const char *, const char *);
3458
3459 jp = curjob;
3460 p = name;
3461 if (!p)
3462 goto currentjob;
3463
3464 if (*p != '%')
3465 goto err;
3466
3467 c = *++p;
3468 if (!c)
3469 goto currentjob;
3470
3471 if (!p[1]) {
3472 if (c == '+' || c == '%') {
3473 currentjob:
3474 err_msg = "No current job";
3475 goto check;
3476 }
3477 if (c == '-') {
3478 if (jp)
3479 jp = jp->prev_job;
3480 err_msg = "No previous job";
3481 check:
3482 if (!jp)
3483 goto err;
3484 goto gotit;
3485 }
3486 }
3487
3488 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003489// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003490 num = atoi(p);
3491 if (num < njobs) {
3492 jp = jobtab + num - 1;
3493 if (jp->used)
3494 goto gotit;
3495 goto err;
3496 }
3497 }
3498
3499 match = prefix;
3500 if (*p == '?') {
3501 match = strstr;
3502 p++;
3503 }
3504
3505 found = 0;
3506 while (1) {
3507 if (!jp)
3508 goto err;
3509 if (match(jp->ps[0].cmd, p)) {
3510 if (found)
3511 goto err;
3512 found = jp;
3513 err_msg = "%s: ambiguous";
3514 }
3515 jp = jp->prev_job;
3516 }
3517
3518 gotit:
3519#if JOBS
3520 err_msg = "job %s not created under job control";
3521 if (getctl && jp->jobctl == 0)
3522 goto err;
3523#endif
3524 return jp;
3525 err:
3526 ash_msg_and_raise_error(err_msg, name);
3527}
3528
3529/*
3530 * Mark a job structure as unused.
3531 */
3532static void
3533freejob(struct job *jp)
3534{
3535 struct procstat *ps;
3536 int i;
3537
3538 INT_OFF;
3539 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3540 if (ps->cmd != nullstr)
3541 free(ps->cmd);
3542 }
3543 if (jp->ps != &jp->ps0)
3544 free(jp->ps);
3545 jp->used = 0;
3546 set_curjob(jp, CUR_DELETE);
3547 INT_ON;
3548}
3549
3550#if JOBS
3551static void
3552xtcsetpgrp(int fd, pid_t pgrp)
3553{
3554 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003555 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003556}
3557
3558/*
3559 * Turn job control on and off.
3560 *
3561 * Note: This code assumes that the third arg to ioctl is a character
3562 * pointer, which is true on Berkeley systems but not System V. Since
3563 * System V doesn't have job control yet, this isn't a problem now.
3564 *
3565 * Called with interrupts off.
3566 */
3567static void
3568setjobctl(int on)
3569{
3570 int fd;
3571 int pgrp;
3572
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003573 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003574 return;
3575 if (on) {
3576 int ofd;
3577 ofd = fd = open(_PATH_TTY, O_RDWR);
3578 if (fd < 0) {
3579 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3580 * That sometimes helps to acquire controlling tty.
3581 * Obviously, a workaround for bugs when someone
3582 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003583 fd = 2;
3584 while (!isatty(fd))
3585 if (--fd < 0)
3586 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003587 }
3588 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003589 if (ofd >= 0)
3590 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003591 if (fd < 0)
3592 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003593 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003594 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003595 do { /* while we are in the background */
3596 pgrp = tcgetpgrp(fd);
3597 if (pgrp < 0) {
3598 out:
3599 ash_msg("can't access tty; job control turned off");
3600 mflag = on = 0;
3601 goto close;
3602 }
3603 if (pgrp == getpgrp())
3604 break;
3605 killpg(0, SIGTTIN);
3606 } while (1);
3607 initialpgrp = pgrp;
3608
3609 setsignal(SIGTSTP);
3610 setsignal(SIGTTOU);
3611 setsignal(SIGTTIN);
3612 pgrp = rootpid;
3613 setpgid(0, pgrp);
3614 xtcsetpgrp(fd, pgrp);
3615 } else {
3616 /* turning job control off */
3617 fd = ttyfd;
3618 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003619 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003620 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003621 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003622 setpgid(0, pgrp);
3623 setsignal(SIGTSTP);
3624 setsignal(SIGTTOU);
3625 setsignal(SIGTTIN);
3626 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003627 if (fd >= 0)
3628 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003629 fd = -1;
3630 }
3631 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003632 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003633}
3634
3635static int
3636killcmd(int argc, char **argv)
3637{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003638 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003639 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003640 do {
3641 if (argv[i][0] == '%') {
3642 struct job *jp = getjob(argv[i], 0);
3643 unsigned pid = jp->ps[0].pid;
3644 /* Enough space for ' -NNN<nul>' */
3645 argv[i] = alloca(sizeof(int)*3 + 3);
3646 /* kill_main has matching code to expect
3647 * leading space. Needed to not confuse
3648 * negative pids with "kill -SIGNAL_NO" syntax */
3649 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003650 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003651 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003652 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003653 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003654}
3655
3656static void
3657showpipe(struct job *jp, FILE *out)
3658{
3659 struct procstat *sp;
3660 struct procstat *spend;
3661
3662 spend = jp->ps + jp->nprocs;
3663 for (sp = jp->ps + 1; sp < spend; sp++)
3664 fprintf(out, " | %s", sp->cmd);
3665 outcslow('\n', out);
3666 flush_stdout_stderr();
3667}
3668
3669
3670static int
3671restartjob(struct job *jp, int mode)
3672{
3673 struct procstat *ps;
3674 int i;
3675 int status;
3676 pid_t pgid;
3677
3678 INT_OFF;
3679 if (jp->state == JOBDONE)
3680 goto out;
3681 jp->state = JOBRUNNING;
3682 pgid = jp->ps->pid;
3683 if (mode == FORK_FG)
3684 xtcsetpgrp(ttyfd, pgid);
3685 killpg(pgid, SIGCONT);
3686 ps = jp->ps;
3687 i = jp->nprocs;
3688 do {
3689 if (WIFSTOPPED(ps->status)) {
3690 ps->status = -1;
3691 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003692 ps++;
3693 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003694 out:
3695 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3696 INT_ON;
3697 return status;
3698}
3699
3700static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003701fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003702{
3703 struct job *jp;
3704 FILE *out;
3705 int mode;
3706 int retval;
3707
3708 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3709 nextopt(nullstr);
3710 argv = argptr;
3711 out = stdout;
3712 do {
3713 jp = getjob(*argv, 1);
3714 if (mode == FORK_BG) {
3715 set_curjob(jp, CUR_RUNNING);
3716 fprintf(out, "[%d] ", jobno(jp));
3717 }
3718 outstr(jp->ps->cmd, out);
3719 showpipe(jp, out);
3720 retval = restartjob(jp, mode);
3721 } while (*argv && *++argv);
3722 return retval;
3723}
3724#endif
3725
3726static int
3727sprint_status(char *s, int status, int sigonly)
3728{
3729 int col;
3730 int st;
3731
3732 col = 0;
3733 if (!WIFEXITED(status)) {
3734#if JOBS
3735 if (WIFSTOPPED(status))
3736 st = WSTOPSIG(status);
3737 else
3738#endif
3739 st = WTERMSIG(status);
3740 if (sigonly) {
3741 if (st == SIGINT || st == SIGPIPE)
3742 goto out;
3743#if JOBS
3744 if (WIFSTOPPED(status))
3745 goto out;
3746#endif
3747 }
3748 st &= 0x7f;
3749 col = fmtstr(s, 32, strsignal(st));
3750 if (WCOREDUMP(status)) {
3751 col += fmtstr(s + col, 16, " (core dumped)");
3752 }
3753 } else if (!sigonly) {
3754 st = WEXITSTATUS(status);
3755 if (st)
3756 col = fmtstr(s, 16, "Done(%d)", st);
3757 else
3758 col = fmtstr(s, 16, "Done");
3759 }
3760 out:
3761 return col;
3762}
3763
3764/*
3765 * Do a wait system call. If job control is compiled in, we accept
3766 * stopped processes. If block is zero, we return a value of zero
3767 * rather than blocking.
3768 *
3769 * System V doesn't have a non-blocking wait system call. It does
3770 * have a SIGCLD signal that is sent to a process when one of it's
3771 * children dies. The obvious way to use SIGCLD would be to install
3772 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3773 * was received, and have waitproc bump another counter when it got
3774 * the status of a process. Waitproc would then know that a wait
3775 * system call would not block if the two counters were different.
3776 * This approach doesn't work because if a process has children that
3777 * have not been waited for, System V will send it a SIGCLD when it
3778 * installs a signal handler for SIGCLD. What this means is that when
3779 * a child exits, the shell will be sent SIGCLD signals continuously
3780 * until is runs out of stack space, unless it does a wait call before
3781 * restoring the signal handler. The code below takes advantage of
3782 * this (mis)feature by installing a signal handler for SIGCLD and
3783 * then checking to see whether it was called. If there are any
3784 * children to be waited for, it will be.
3785 *
3786 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3787 * waits at all. In this case, the user will not be informed when
3788 * a background process until the next time she runs a real program
3789 * (as opposed to running a builtin command or just typing return),
3790 * and the jobs command may give out of date information.
3791 */
3792static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003793waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003794{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003796 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003797 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003798#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003799 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3800 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003801}
3802
3803/*
3804 * Wait for a process to terminate.
3805 */
3806static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003807dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003808{
3809 int pid;
3810 int status;
3811 struct job *jp;
3812 struct job *thisjob;
3813 int state;
3814
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003815 TRACE(("dowait(%d) called\n", wait_flags));
3816 pid = waitproc(wait_flags, &status);
3817 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003818 if (pid <= 0) {
3819 /* If we were doing blocking wait and (probably) got EINTR,
3820 * check for pending sigs received while waiting.
3821 * (NB: can be moved into callers if needed) */
3822 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3823 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003824 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003825 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003826 INT_OFF;
3827 thisjob = NULL;
3828 for (jp = curjob; jp; jp = jp->prev_job) {
3829 struct procstat *sp;
3830 struct procstat *spend;
3831 if (jp->state == JOBDONE)
3832 continue;
3833 state = JOBDONE;
3834 spend = jp->ps + jp->nprocs;
3835 sp = jp->ps;
3836 do {
3837 if (sp->pid == pid) {
3838 TRACE(("Job %d: changing status of proc %d "
3839 "from 0x%x to 0x%x\n",
3840 jobno(jp), pid, sp->status, status));
3841 sp->status = status;
3842 thisjob = jp;
3843 }
3844 if (sp->status == -1)
3845 state = JOBRUNNING;
3846#if JOBS
3847 if (state == JOBRUNNING)
3848 continue;
3849 if (WIFSTOPPED(sp->status)) {
3850 jp->stopstatus = sp->status;
3851 state = JOBSTOPPED;
3852 }
3853#endif
3854 } while (++sp < spend);
3855 if (thisjob)
3856 goto gotjob;
3857 }
3858#if JOBS
3859 if (!WIFSTOPPED(status))
3860#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003861 jobless--;
3862 goto out;
3863
3864 gotjob:
3865 if (state != JOBRUNNING) {
3866 thisjob->changed = 1;
3867
3868 if (thisjob->state != state) {
3869 TRACE(("Job %d: changing state from %d to %d\n",
3870 jobno(thisjob), thisjob->state, state));
3871 thisjob->state = state;
3872#if JOBS
3873 if (state == JOBSTOPPED) {
3874 set_curjob(thisjob, CUR_STOPPED);
3875 }
3876#endif
3877 }
3878 }
3879
3880 out:
3881 INT_ON;
3882
3883 if (thisjob && thisjob == job) {
3884 char s[48 + 1];
3885 int len;
3886
3887 len = sprint_status(s, status, 1);
3888 if (len) {
3889 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003890 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003891 out2str(s);
3892 }
3893 }
3894 return pid;
3895}
3896
3897#if JOBS
3898static void
3899showjob(FILE *out, struct job *jp, int mode)
3900{
3901 struct procstat *ps;
3902 struct procstat *psend;
3903 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003904 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003905 char s[80];
3906
3907 ps = jp->ps;
3908
3909 if (mode & SHOW_PGID) {
3910 /* just output process (group) id of pipeline */
3911 fprintf(out, "%d\n", ps->pid);
3912 return;
3913 }
3914
3915 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003916 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003917
3918 if (jp == curjob)
3919 s[col - 2] = '+';
3920 else if (curjob && jp == curjob->prev_job)
3921 s[col - 2] = '-';
3922
3923 if (mode & SHOW_PID)
3924 col += fmtstr(s + col, 16, "%d ", ps->pid);
3925
3926 psend = ps + jp->nprocs;
3927
3928 if (jp->state == JOBRUNNING) {
3929 strcpy(s + col, "Running");
3930 col += sizeof("Running") - 1;
3931 } else {
3932 int status = psend[-1].status;
3933 if (jp->state == JOBSTOPPED)
3934 status = jp->stopstatus;
3935 col += sprint_status(s + col, status, 0);
3936 }
3937
3938 goto start;
3939
3940 do {
3941 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003942 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003943 start:
3944 fprintf(out, "%s%*c%s",
3945 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3946 );
3947 if (!(mode & SHOW_PID)) {
3948 showpipe(jp, out);
3949 break;
3950 }
3951 if (++ps == psend) {
3952 outcslow('\n', out);
3953 break;
3954 }
3955 } while (1);
3956
3957 jp->changed = 0;
3958
3959 if (jp->state == JOBDONE) {
3960 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3961 freejob(jp);
3962 }
3963}
3964
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003965/*
3966 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3967 * statuses have changed since the last call to showjobs.
3968 */
3969static void
3970showjobs(FILE *out, int mode)
3971{
3972 struct job *jp;
3973
3974 TRACE(("showjobs(%x) called\n", mode));
3975
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003976 /* If not even one job changed, there is nothing to do */
3977 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003978 continue;
3979
3980 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003981 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003982 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003983 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003984 }
3985}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003986
3987static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003988jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003989{
3990 int mode, m;
3991
3992 mode = 0;
3993 while ((m = nextopt("lp"))) {
3994 if (m == 'l')
3995 mode = SHOW_PID;
3996 else
3997 mode = SHOW_PGID;
3998 }
3999
4000 argv = argptr;
4001 if (*argv) {
4002 do
4003 showjob(stdout, getjob(*argv,0), mode);
4004 while (*++argv);
4005 } else
4006 showjobs(stdout, mode);
4007
4008 return 0;
4009}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004010#endif /* JOBS */
4011
4012static int
4013getstatus(struct job *job)
4014{
4015 int status;
4016 int retval;
4017
4018 status = job->ps[job->nprocs - 1].status;
4019 retval = WEXITSTATUS(status);
4020 if (!WIFEXITED(status)) {
4021#if JOBS
4022 retval = WSTOPSIG(status);
4023 if (!WIFSTOPPED(status))
4024#endif
4025 {
4026 /* XXX: limits number of signals */
4027 retval = WTERMSIG(status);
4028#if JOBS
4029 if (retval == SIGINT)
4030 job->sigint = 1;
4031#endif
4032 }
4033 retval += 128;
4034 }
4035 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4036 jobno(job), job->nprocs, status, retval));
4037 return retval;
4038}
4039
4040static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004041waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004042{
4043 struct job *job;
4044 int retval;
4045 struct job *jp;
4046
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004047// exsig++;
4048// xbarrier();
4049 if (pendingsig)
4050 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051
4052 nextopt(nullstr);
4053 retval = 0;
4054
4055 argv = argptr;
4056 if (!*argv) {
4057 /* wait for all jobs */
4058 for (;;) {
4059 jp = curjob;
4060 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004061 if (!jp) /* no running procs */
4062 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004063 if (jp->state == JOBRUNNING)
4064 break;
4065 jp->waited = 1;
4066 jp = jp->prev_job;
4067 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004068 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004069 }
4070 }
4071
4072 retval = 127;
4073 do {
4074 if (**argv != '%') {
4075 pid_t pid = number(*argv);
4076 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004077 while (1) {
4078 if (!job)
4079 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004080 if (job->ps[job->nprocs - 1].pid == pid)
4081 break;
4082 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004083 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004084 } else
4085 job = getjob(*argv, 0);
4086 /* loop until process terminated or stopped */
4087 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004088 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004089 job->waited = 1;
4090 retval = getstatus(job);
4091 repeat:
4092 ;
4093 } while (*++argv);
4094
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004095 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 return retval;
4097}
4098
4099static struct job *
4100growjobtab(void)
4101{
4102 size_t len;
4103 ptrdiff_t offset;
4104 struct job *jp, *jq;
4105
4106 len = njobs * sizeof(*jp);
4107 jq = jobtab;
4108 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4109
4110 offset = (char *)jp - (char *)jq;
4111 if (offset) {
4112 /* Relocate pointers */
4113 size_t l = len;
4114
4115 jq = (struct job *)((char *)jq + l);
4116 while (l) {
4117 l -= sizeof(*jp);
4118 jq--;
4119#define joff(p) ((struct job *)((char *)(p) + l))
4120#define jmove(p) (p) = (void *)((char *)(p) + offset)
4121 if (joff(jp)->ps == &jq->ps0)
4122 jmove(joff(jp)->ps);
4123 if (joff(jp)->prev_job)
4124 jmove(joff(jp)->prev_job);
4125 }
4126 if (curjob)
4127 jmove(curjob);
4128#undef joff
4129#undef jmove
4130 }
4131
4132 njobs += 4;
4133 jobtab = jp;
4134 jp = (struct job *)((char *)jp + len);
4135 jq = jp + 3;
4136 do {
4137 jq->used = 0;
4138 } while (--jq >= jp);
4139 return jp;
4140}
4141
4142/*
4143 * Return a new job structure.
4144 * Called with interrupts off.
4145 */
4146static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004147makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004148{
4149 int i;
4150 struct job *jp;
4151
4152 for (i = njobs, jp = jobtab; ; jp++) {
4153 if (--i < 0) {
4154 jp = growjobtab();
4155 break;
4156 }
4157 if (jp->used == 0)
4158 break;
4159 if (jp->state != JOBDONE || !jp->waited)
4160 continue;
4161#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004162 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004163 continue;
4164#endif
4165 freejob(jp);
4166 break;
4167 }
4168 memset(jp, 0, sizeof(*jp));
4169#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004170 /* jp->jobctl is a bitfield.
4171 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004172 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004173 jp->jobctl = 1;
4174#endif
4175 jp->prev_job = curjob;
4176 curjob = jp;
4177 jp->used = 1;
4178 jp->ps = &jp->ps0;
4179 if (nprocs > 1) {
4180 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4181 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004182 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004183 jobno(jp)));
4184 return jp;
4185}
4186
4187#if JOBS
4188/*
4189 * Return a string identifying a command (to be printed by the
4190 * jobs command).
4191 */
4192static char *cmdnextc;
4193
4194static void
4195cmdputs(const char *s)
4196{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004197 static const char vstype[VSTYPE + 1][3] = {
4198 "", "}", "-", "+", "?", "=",
4199 "%", "%%", "#", "##"
4200 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4201 };
4202
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004203 const char *p, *str;
4204 char c, cc[2] = " ";
4205 char *nextc;
4206 int subtype = 0;
4207 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004208
4209 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4210 p = s;
4211 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004212 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004213 switch (c) {
4214 case CTLESC:
4215 c = *p++;
4216 break;
4217 case CTLVAR:
4218 subtype = *p++;
4219 if ((subtype & VSTYPE) == VSLENGTH)
4220 str = "${#";
4221 else
4222 str = "${";
4223 if (!(subtype & VSQUOTE) == !(quoted & 1))
4224 goto dostr;
4225 quoted ^= 1;
4226 c = '"';
4227 break;
4228 case CTLENDVAR:
4229 str = "\"}" + !(quoted & 1);
4230 quoted >>= 1;
4231 subtype = 0;
4232 goto dostr;
4233 case CTLBACKQ:
4234 str = "$(...)";
4235 goto dostr;
4236 case CTLBACKQ+CTLQUOTE:
4237 str = "\"$(...)\"";
4238 goto dostr;
4239#if ENABLE_ASH_MATH_SUPPORT
4240 case CTLARI:
4241 str = "$((";
4242 goto dostr;
4243 case CTLENDARI:
4244 str = "))";
4245 goto dostr;
4246#endif
4247 case CTLQUOTEMARK:
4248 quoted ^= 1;
4249 c = '"';
4250 break;
4251 case '=':
4252 if (subtype == 0)
4253 break;
4254 if ((subtype & VSTYPE) != VSNORMAL)
4255 quoted <<= 1;
4256 str = vstype[subtype & VSTYPE];
4257 if (subtype & VSNUL)
4258 c = ':';
4259 else
4260 goto checkstr;
4261 break;
4262 case '\'':
4263 case '\\':
4264 case '"':
4265 case '$':
4266 /* These can only happen inside quotes */
4267 cc[0] = c;
4268 str = cc;
4269 c = '\\';
4270 break;
4271 default:
4272 break;
4273 }
4274 USTPUTC(c, nextc);
4275 checkstr:
4276 if (!str)
4277 continue;
4278 dostr:
4279 while ((c = *str++)) {
4280 USTPUTC(c, nextc);
4281 }
4282 }
4283 if (quoted & 1) {
4284 USTPUTC('"', nextc);
4285 }
4286 *nextc = 0;
4287 cmdnextc = nextc;
4288}
4289
4290/* cmdtxt() and cmdlist() call each other */
4291static void cmdtxt(union node *n);
4292
4293static void
4294cmdlist(union node *np, int sep)
4295{
4296 for (; np; np = np->narg.next) {
4297 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004298 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004299 cmdtxt(np);
4300 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004301 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004302 }
4303}
4304
4305static void
4306cmdtxt(union node *n)
4307{
4308 union node *np;
4309 struct nodelist *lp;
4310 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004311
4312 if (!n)
4313 return;
4314 switch (n->type) {
4315 default:
4316#if DEBUG
4317 abort();
4318#endif
4319 case NPIPE:
4320 lp = n->npipe.cmdlist;
4321 for (;;) {
4322 cmdtxt(lp->n);
4323 lp = lp->next;
4324 if (!lp)
4325 break;
4326 cmdputs(" | ");
4327 }
4328 break;
4329 case NSEMI:
4330 p = "; ";
4331 goto binop;
4332 case NAND:
4333 p = " && ";
4334 goto binop;
4335 case NOR:
4336 p = " || ";
4337 binop:
4338 cmdtxt(n->nbinary.ch1);
4339 cmdputs(p);
4340 n = n->nbinary.ch2;
4341 goto donode;
4342 case NREDIR:
4343 case NBACKGND:
4344 n = n->nredir.n;
4345 goto donode;
4346 case NNOT:
4347 cmdputs("!");
4348 n = n->nnot.com;
4349 donode:
4350 cmdtxt(n);
4351 break;
4352 case NIF:
4353 cmdputs("if ");
4354 cmdtxt(n->nif.test);
4355 cmdputs("; then ");
4356 n = n->nif.ifpart;
4357 if (n->nif.elsepart) {
4358 cmdtxt(n);
4359 cmdputs("; else ");
4360 n = n->nif.elsepart;
4361 }
4362 p = "; fi";
4363 goto dotail;
4364 case NSUBSHELL:
4365 cmdputs("(");
4366 n = n->nredir.n;
4367 p = ")";
4368 goto dotail;
4369 case NWHILE:
4370 p = "while ";
4371 goto until;
4372 case NUNTIL:
4373 p = "until ";
4374 until:
4375 cmdputs(p);
4376 cmdtxt(n->nbinary.ch1);
4377 n = n->nbinary.ch2;
4378 p = "; done";
4379 dodo:
4380 cmdputs("; do ");
4381 dotail:
4382 cmdtxt(n);
4383 goto dotail2;
4384 case NFOR:
4385 cmdputs("for ");
4386 cmdputs(n->nfor.var);
4387 cmdputs(" in ");
4388 cmdlist(n->nfor.args, 1);
4389 n = n->nfor.body;
4390 p = "; done";
4391 goto dodo;
4392 case NDEFUN:
4393 cmdputs(n->narg.text);
4394 p = "() { ... }";
4395 goto dotail2;
4396 case NCMD:
4397 cmdlist(n->ncmd.args, 1);
4398 cmdlist(n->ncmd.redirect, 0);
4399 break;
4400 case NARG:
4401 p = n->narg.text;
4402 dotail2:
4403 cmdputs(p);
4404 break;
4405 case NHERE:
4406 case NXHERE:
4407 p = "<<...";
4408 goto dotail2;
4409 case NCASE:
4410 cmdputs("case ");
4411 cmdputs(n->ncase.expr->narg.text);
4412 cmdputs(" in ");
4413 for (np = n->ncase.cases; np; np = np->nclist.next) {
4414 cmdtxt(np->nclist.pattern);
4415 cmdputs(") ");
4416 cmdtxt(np->nclist.body);
4417 cmdputs(";; ");
4418 }
4419 p = "esac";
4420 goto dotail2;
4421 case NTO:
4422 p = ">";
4423 goto redir;
4424 case NCLOBBER:
4425 p = ">|";
4426 goto redir;
4427 case NAPPEND:
4428 p = ">>";
4429 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004430#if ENABLE_ASH_BASH_COMPAT
4431 case NTO2:
4432#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004433 case NTOFD:
4434 p = ">&";
4435 goto redir;
4436 case NFROM:
4437 p = "<";
4438 goto redir;
4439 case NFROMFD:
4440 p = "<&";
4441 goto redir;
4442 case NFROMTO:
4443 p = "<>";
4444 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004445 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004446 cmdputs(p);
4447 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004448 cmdputs(utoa(n->ndup.dupfd));
4449 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004450 }
4451 n = n->nfile.fname;
4452 goto donode;
4453 }
4454}
4455
4456static char *
4457commandtext(union node *n)
4458{
4459 char *name;
4460
4461 STARTSTACKSTR(cmdnextc);
4462 cmdtxt(n);
4463 name = stackblock();
4464 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4465 name, cmdnextc, cmdnextc));
4466 return ckstrdup(name);
4467}
4468#endif /* JOBS */
4469
4470/*
4471 * Fork off a subshell. If we are doing job control, give the subshell its
4472 * own process group. Jp is a job structure that the job is to be added to.
4473 * N is the command that will be evaluated by the child. Both jp and n may
4474 * be NULL. The mode parameter can be one of the following:
4475 * FORK_FG - Fork off a foreground process.
4476 * FORK_BG - Fork off a background process.
4477 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4478 * process group even if job control is on.
4479 *
4480 * When job control is turned off, background processes have their standard
4481 * input redirected to /dev/null (except for the second and later processes
4482 * in a pipeline).
4483 *
4484 * Called with interrupts off.
4485 */
4486/*
4487 * Clear traps on a fork.
4488 */
4489static void
4490clear_traps(void)
4491{
4492 char **tp;
4493
4494 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004495 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004496 INT_OFF;
4497 free(*tp);
4498 *tp = NULL;
4499 if (tp != &trap[0])
4500 setsignal(tp - trap);
4501 INT_ON;
4502 }
4503 }
4504}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004505
4506/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004507static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004508
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004509/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004510static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004511forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004512{
4513 int oldlvl;
4514
4515 TRACE(("Child shell %d\n", getpid()));
4516 oldlvl = shlvl;
4517 shlvl++;
4518
4519 closescript();
4520 clear_traps();
4521#if JOBS
4522 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004523 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004524 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4525 pid_t pgrp;
4526
4527 if (jp->nprocs == 0)
4528 pgrp = getpid();
4529 else
4530 pgrp = jp->ps[0].pid;
4531 /* This can fail because we are doing it in the parent also */
4532 (void)setpgid(0, pgrp);
4533 if (mode == FORK_FG)
4534 xtcsetpgrp(ttyfd, pgrp);
4535 setsignal(SIGTSTP);
4536 setsignal(SIGTTOU);
4537 } else
4538#endif
4539 if (mode == FORK_BG) {
4540 ignoresig(SIGINT);
4541 ignoresig(SIGQUIT);
4542 if (jp->nprocs == 0) {
4543 close(0);
4544 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004545 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004546 }
4547 }
4548 if (!oldlvl && iflag) {
4549 setsignal(SIGINT);
4550 setsignal(SIGQUIT);
4551 setsignal(SIGTERM);
4552 }
4553 for (jp = curjob; jp; jp = jp->prev_job)
4554 freejob(jp);
4555 jobless = 0;
4556}
4557
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004558/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004559#if !JOBS
4560#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4561#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004562static void
4563forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4564{
4565 TRACE(("In parent shell: child = %d\n", pid));
4566 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004567 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4568 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004569 jobless++;
4570 return;
4571 }
4572#if JOBS
4573 if (mode != FORK_NOJOB && jp->jobctl) {
4574 int pgrp;
4575
4576 if (jp->nprocs == 0)
4577 pgrp = pid;
4578 else
4579 pgrp = jp->ps[0].pid;
4580 /* This can fail because we are doing it in the child also */
4581 setpgid(pid, pgrp);
4582 }
4583#endif
4584 if (mode == FORK_BG) {
4585 backgndpid = pid; /* set $! */
4586 set_curjob(jp, CUR_RUNNING);
4587 }
4588 if (jp) {
4589 struct procstat *ps = &jp->ps[jp->nprocs++];
4590 ps->pid = pid;
4591 ps->status = -1;
4592 ps->cmd = nullstr;
4593#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004594 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004595 ps->cmd = commandtext(n);
4596#endif
4597 }
4598}
4599
4600static int
4601forkshell(struct job *jp, union node *n, int mode)
4602{
4603 int pid;
4604
4605 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4606 pid = fork();
4607 if (pid < 0) {
4608 TRACE(("Fork failed, errno=%d", errno));
4609 if (jp)
4610 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004611 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004612 }
4613 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004614 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004615 else
4616 forkparent(jp, n, mode, pid);
4617 return pid;
4618}
4619
4620/*
4621 * Wait for job to finish.
4622 *
4623 * Under job control we have the problem that while a child process is
4624 * running interrupts generated by the user are sent to the child but not
4625 * to the shell. This means that an infinite loop started by an inter-
4626 * active user may be hard to kill. With job control turned off, an
4627 * interactive user may place an interactive program inside a loop. If
4628 * the interactive program catches interrupts, the user doesn't want
4629 * these interrupts to also abort the loop. The approach we take here
4630 * is to have the shell ignore interrupt signals while waiting for a
4631 * foreground process to terminate, and then send itself an interrupt
4632 * signal if the child process was terminated by an interrupt signal.
4633 * Unfortunately, some programs want to do a bit of cleanup and then
4634 * exit on interrupt; unless these processes terminate themselves by
4635 * sending a signal to themselves (instead of calling exit) they will
4636 * confuse this approach.
4637 *
4638 * Called with interrupts off.
4639 */
4640static int
4641waitforjob(struct job *jp)
4642{
4643 int st;
4644
4645 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4646 while (jp->state == JOBRUNNING) {
4647 dowait(DOWAIT_BLOCK, jp);
4648 }
4649 st = getstatus(jp);
4650#if JOBS
4651 if (jp->jobctl) {
4652 xtcsetpgrp(ttyfd, rootpid);
4653 /*
4654 * This is truly gross.
4655 * If we're doing job control, then we did a TIOCSPGRP which
4656 * caused us (the shell) to no longer be in the controlling
4657 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4658 * intuit from the subprocess exit status whether a SIGINT
4659 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4660 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004661 if (jp->sigint) /* TODO: do the same with all signals */
4662 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004663 }
4664 if (jp->state == JOBDONE)
4665#endif
4666 freejob(jp);
4667 return st;
4668}
4669
4670/*
4671 * return 1 if there are stopped jobs, otherwise 0
4672 */
4673static int
4674stoppedjobs(void)
4675{
4676 struct job *jp;
4677 int retval;
4678
4679 retval = 0;
4680 if (job_warning)
4681 goto out;
4682 jp = curjob;
4683 if (jp && jp->state == JOBSTOPPED) {
4684 out2str("You have stopped jobs.\n");
4685 job_warning = 2;
4686 retval++;
4687 }
4688 out:
4689 return retval;
4690}
4691
4692
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004693/* ============ redir.c
4694 *
4695 * Code for dealing with input/output redirection.
4696 */
4697
4698#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004699#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004700
4701/*
4702 * Open a file in noclobber mode.
4703 * The code was copied from bash.
4704 */
4705static int
4706noclobberopen(const char *fname)
4707{
4708 int r, fd;
4709 struct stat finfo, finfo2;
4710
4711 /*
4712 * If the file exists and is a regular file, return an error
4713 * immediately.
4714 */
4715 r = stat(fname, &finfo);
4716 if (r == 0 && S_ISREG(finfo.st_mode)) {
4717 errno = EEXIST;
4718 return -1;
4719 }
4720
4721 /*
4722 * If the file was not present (r != 0), make sure we open it
4723 * exclusively so that if it is created before we open it, our open
4724 * will fail. Make sure that we do not truncate an existing file.
4725 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4726 * file was not a regular file, we leave O_EXCL off.
4727 */
4728 if (r != 0)
4729 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4730 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4731
4732 /* If the open failed, return the file descriptor right away. */
4733 if (fd < 0)
4734 return fd;
4735
4736 /*
4737 * OK, the open succeeded, but the file may have been changed from a
4738 * non-regular file to a regular file between the stat and the open.
4739 * We are assuming that the O_EXCL open handles the case where FILENAME
4740 * did not exist and is symlinked to an existing file between the stat
4741 * and open.
4742 */
4743
4744 /*
4745 * If we can open it and fstat the file descriptor, and neither check
4746 * revealed that it was a regular file, and the file has not been
4747 * replaced, return the file descriptor.
4748 */
4749 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4750 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4751 return fd;
4752
4753 /* The file has been replaced. badness. */
4754 close(fd);
4755 errno = EEXIST;
4756 return -1;
4757}
4758
4759/*
4760 * Handle here documents. Normally we fork off a process to write the
4761 * data to a pipe. If the document is short, we can stuff the data in
4762 * the pipe without forking.
4763 */
4764/* openhere needs this forward reference */
4765static void expandhere(union node *arg, int fd);
4766static int
4767openhere(union node *redir)
4768{
4769 int pip[2];
4770 size_t len = 0;
4771
4772 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004773 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004774 if (redir->type == NHERE) {
4775 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004776 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004777 full_write(pip[1], redir->nhere.doc->narg.text, len);
4778 goto out;
4779 }
4780 }
4781 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004782 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004783 close(pip[0]);
4784 signal(SIGINT, SIG_IGN);
4785 signal(SIGQUIT, SIG_IGN);
4786 signal(SIGHUP, SIG_IGN);
4787#ifdef SIGTSTP
4788 signal(SIGTSTP, SIG_IGN);
4789#endif
4790 signal(SIGPIPE, SIG_DFL);
4791 if (redir->type == NHERE)
4792 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004793 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004794 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004795 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004796 }
4797 out:
4798 close(pip[1]);
4799 return pip[0];
4800}
4801
4802static int
4803openredirect(union node *redir)
4804{
4805 char *fname;
4806 int f;
4807
4808 switch (redir->nfile.type) {
4809 case NFROM:
4810 fname = redir->nfile.expfname;
4811 f = open(fname, O_RDONLY);
4812 if (f < 0)
4813 goto eopen;
4814 break;
4815 case NFROMTO:
4816 fname = redir->nfile.expfname;
4817 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4818 if (f < 0)
4819 goto ecreate;
4820 break;
4821 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004822#if ENABLE_ASH_BASH_COMPAT
4823 case NTO2:
4824#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004825 /* Take care of noclobber mode. */
4826 if (Cflag) {
4827 fname = redir->nfile.expfname;
4828 f = noclobberopen(fname);
4829 if (f < 0)
4830 goto ecreate;
4831 break;
4832 }
4833 /* FALLTHROUGH */
4834 case NCLOBBER:
4835 fname = redir->nfile.expfname;
4836 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4837 if (f < 0)
4838 goto ecreate;
4839 break;
4840 case NAPPEND:
4841 fname = redir->nfile.expfname;
4842 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4843 if (f < 0)
4844 goto ecreate;
4845 break;
4846 default:
4847#if DEBUG
4848 abort();
4849#endif
4850 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004851/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004852// case NTOFD:
4853// case NFROMFD:
4854// f = -1;
4855// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004856 case NHERE:
4857 case NXHERE:
4858 f = openhere(redir);
4859 break;
4860 }
4861
4862 return f;
4863 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004864 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004865 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004866 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004867}
4868
4869/*
4870 * Copy a file descriptor to be >= to. Returns -1
4871 * if the source file descriptor is closed, EMPTY if there are no unused
4872 * file descriptors left.
4873 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004874/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4875 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004876enum {
4877 COPYFD_EXACT = (int)~(INT_MAX),
4878 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4879};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004880static int
4881copyfd(int from, int to)
4882{
4883 int newfd;
4884
Denis Vlasenko5a867312008-07-24 19:46:38 +00004885 if (to & COPYFD_EXACT) {
4886 to &= ~COPYFD_EXACT;
4887 /*if (from != to)*/
4888 newfd = dup2(from, to);
4889 } else {
4890 newfd = fcntl(from, F_DUPFD, to);
4891 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004892 if (newfd < 0) {
4893 if (errno == EMFILE)
4894 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004895 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004896 ash_msg_and_raise_error("%d: %m", from);
4897 }
4898 return newfd;
4899}
4900
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004901/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004902struct two_fd_t {
4903 int orig, copy;
4904};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004905struct redirtab {
4906 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004907 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004908 int pair_count;
4909 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004910};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004911#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004912
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004913static int need_to_remember(struct redirtab *rp, int fd)
4914{
4915 int i;
4916
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004917 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004918 return 0;
4919
4920 for (i = 0; i < rp->pair_count; i++) {
4921 if (rp->two_fd[i].orig == fd) {
4922 /* already remembered */
4923 return 0;
4924 }
4925 }
4926 return 1;
4927}
4928
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004929/* "hidden" fd is a fd used to read scripts, or a copy of such */
4930static int is_hidden_fd(struct redirtab *rp, int fd)
4931{
4932 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004933 struct parsefile *pf;
4934
4935 if (fd == -1)
4936 return 0;
4937 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004938 while (pf) {
4939 if (fd == pf->fd) {
4940 return 1;
4941 }
4942 pf = pf->prev;
4943 }
4944 if (!rp)
4945 return 0;
4946 fd |= COPYFD_RESTORE;
4947 for (i = 0; i < rp->pair_count; i++) {
4948 if (rp->two_fd[i].copy == fd) {
4949 return 1;
4950 }
4951 }
4952 return 0;
4953}
4954
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004955/*
4956 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4957 * old file descriptors are stashed away so that the redirection can be
4958 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4959 * standard output, and the standard error if it becomes a duplicate of
4960 * stdout, is saved in memory.
4961 */
4962/* flags passed to redirect */
4963#define REDIR_PUSH 01 /* save previous values of file descriptors */
4964#define REDIR_SAVEFD2 03 /* set preverrout */
4965static void
4966redirect(union node *redir, int flags)
4967{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004968 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004969 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004970 int i;
4971 int fd;
4972 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004973 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004974
Denis Vlasenko01631112007-12-16 17:20:38 +00004975 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004976 if (!redir) {
4977 return;
4978 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004979
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004980 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004981 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004982 INT_OFF;
4983 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004984 union node *tmp = redir;
4985 do {
4986 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004987#if ENABLE_ASH_BASH_COMPAT
4988 if (redir->nfile.type == NTO2)
4989 sv_pos++;
4990#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004991 tmp = tmp->nfile.next;
4992 } while (tmp);
4993 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004994 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004995 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004996 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004997 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004998 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004999 while (sv_pos > 0) {
5000 sv_pos--;
5001 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5002 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005003 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005004
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005005 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005006 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005007 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005008 int right_fd = redir->ndup.dupfd;
5009 /* redirect from/to same file descriptor? */
5010 if (right_fd == fd)
5011 continue;
5012 /* echo >&10 and 10 is a fd opened to the sh script? */
5013 if (is_hidden_fd(sv, right_fd)) {
5014 errno = EBADF; /* as if it is closed */
5015 ash_msg_and_raise_error("%d: %m", right_fd);
5016 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005017 newfd = -1;
5018 } else {
5019 newfd = openredirect(redir); /* always >= 0 */
5020 if (fd == newfd) {
5021 /* Descriptor wasn't open before redirect.
5022 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005023 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005024 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005025 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005026 continue;
5027 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005028 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005029#if ENABLE_ASH_BASH_COMPAT
5030 redirect_more:
5031#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005032 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005033 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005034 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005035/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5036 * are closed in popredir() in the child, preventing them from leaking
5037 * into child. (popredir() also cleans up the mess in case of failures)
5038 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005039 if (i == -1) {
5040 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005041 if (i != EBADF) {
5042 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005043 if (newfd >= 0)
5044 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 errno = i;
5046 ash_msg_and_raise_error("%d: %m", fd);
5047 /* NOTREACHED */
5048 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005049 /* EBADF: it is not open - good, remember to close it */
5050 remember_to_close:
5051 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005052 } else { /* fd is open, save its copy */
5053 /* "exec fd>&-" should not close fds
5054 * which point to script file(s).
5055 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005056 if (is_hidden_fd(sv, fd))
5057 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005058 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005059 if (fd == 2)
5060 copied_fd2 = i;
5061 sv->two_fd[sv_pos].orig = fd;
5062 sv->two_fd[sv_pos].copy = i;
5063 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005064 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005065 if (newfd < 0) {
5066 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005067 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005068 close(fd);
5069 } else {
5070 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005071 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005072 } else if (fd != newfd) { /* move newfd to fd */
5073 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005074#if ENABLE_ASH_BASH_COMPAT
5075 if (!(redir->nfile.type == NTO2 && fd == 2))
5076#endif
5077 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005078 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005079#if ENABLE_ASH_BASH_COMPAT
5080 if (redir->nfile.type == NTO2 && fd == 1) {
5081 /* We already redirected it to fd 1, now copy it to 2 */
5082 newfd = 1;
5083 fd = 2;
5084 goto redirect_more;
5085 }
5086#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005087 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005088
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005089 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005090 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5091 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005092}
5093
5094/*
5095 * Undo the effects of the last redirection.
5096 */
5097static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005098popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005099{
5100 struct redirtab *rp;
5101 int i;
5102
Denis Vlasenko01631112007-12-16 17:20:38 +00005103 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005104 return;
5105 INT_OFF;
5106 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005107 for (i = 0; i < rp->pair_count; i++) {
5108 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005109 int copy = rp->two_fd[i].copy;
5110 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005111 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005112 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005113 continue;
5114 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005115 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005116 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005117 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005118 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005119 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005120 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005121 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005122 }
5123 }
5124 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005125 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005126 free(rp);
5127 INT_ON;
5128}
5129
5130/*
5131 * Undo all redirections. Called on error or interrupt.
5132 */
5133
5134/*
5135 * Discard all saved file descriptors.
5136 */
5137static void
5138clearredir(int drop)
5139{
5140 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005141 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005142 if (!redirlist)
5143 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005144 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005145 }
5146}
5147
5148static int
5149redirectsafe(union node *redir, int flags)
5150{
5151 int err;
5152 volatile int saveint;
5153 struct jmploc *volatile savehandler = exception_handler;
5154 struct jmploc jmploc;
5155
5156 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005157 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5158 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005159 if (!err) {
5160 exception_handler = &jmploc;
5161 redirect(redir, flags);
5162 }
5163 exception_handler = savehandler;
5164 if (err && exception != EXERROR)
5165 longjmp(exception_handler->loc, 1);
5166 RESTORE_INT(saveint);
5167 return err;
5168}
5169
5170
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005171/* ============ Routines to expand arguments to commands
5172 *
5173 * We have to deal with backquotes, shell variables, and file metacharacters.
5174 */
5175
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005176#if ENABLE_ASH_MATH_SUPPORT_64
5177typedef int64_t arith_t;
5178#define arith_t_type long long
5179#else
5180typedef long arith_t;
5181#define arith_t_type long
5182#endif
5183
5184#if ENABLE_ASH_MATH_SUPPORT
5185static arith_t dash_arith(const char *);
5186static arith_t arith(const char *expr, int *perrcode);
5187#endif
5188
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005189/*
5190 * expandarg flags
5191 */
5192#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5193#define EXP_TILDE 0x2 /* do normal tilde expansion */
5194#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5195#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5196#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5197#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5198#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5199#define EXP_WORD 0x80 /* expand word in parameter expansion */
5200#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5201/*
5202 * _rmescape() flags
5203 */
5204#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5205#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5206#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5207#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5208#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5209
5210/*
5211 * Structure specifying which parts of the string should be searched
5212 * for IFS characters.
5213 */
5214struct ifsregion {
5215 struct ifsregion *next; /* next region in list */
5216 int begoff; /* offset of start of region */
5217 int endoff; /* offset of end of region */
5218 int nulonly; /* search for nul bytes only */
5219};
5220
5221struct arglist {
5222 struct strlist *list;
5223 struct strlist **lastp;
5224};
5225
5226/* output of current string */
5227static char *expdest;
5228/* list of back quote expressions */
5229static struct nodelist *argbackq;
5230/* first struct in list of ifs regions */
5231static struct ifsregion ifsfirst;
5232/* last struct in list */
5233static struct ifsregion *ifslastp;
5234/* holds expanded arg list */
5235static struct arglist exparg;
5236
5237/*
5238 * Our own itoa().
5239 */
5240static int
5241cvtnum(arith_t num)
5242{
5243 int len;
5244
5245 expdest = makestrspace(32, expdest);
5246#if ENABLE_ASH_MATH_SUPPORT_64
5247 len = fmtstr(expdest, 32, "%lld", (long long) num);
5248#else
5249 len = fmtstr(expdest, 32, "%ld", num);
5250#endif
5251 STADJUST(len, expdest);
5252 return len;
5253}
5254
5255static size_t
5256esclen(const char *start, const char *p)
5257{
5258 size_t esc = 0;
5259
5260 while (p > start && *--p == CTLESC) {
5261 esc++;
5262 }
5263 return esc;
5264}
5265
5266/*
5267 * Remove any CTLESC characters from a string.
5268 */
5269static char *
5270_rmescapes(char *str, int flag)
5271{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005272 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005273
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005274 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005275 unsigned inquotes;
5276 int notescaped;
5277 int globbing;
5278
5279 p = strpbrk(str, qchars);
5280 if (!p) {
5281 return str;
5282 }
5283 q = p;
5284 r = str;
5285 if (flag & RMESCAPE_ALLOC) {
5286 size_t len = p - str;
5287 size_t fulllen = len + strlen(p) + 1;
5288
5289 if (flag & RMESCAPE_GROW) {
5290 r = makestrspace(fulllen, expdest);
5291 } else if (flag & RMESCAPE_HEAP) {
5292 r = ckmalloc(fulllen);
5293 } else {
5294 r = stalloc(fulllen);
5295 }
5296 q = r;
5297 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005298 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005299 }
5300 }
5301 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5302 globbing = flag & RMESCAPE_GLOB;
5303 notescaped = globbing;
5304 while (*p) {
5305 if (*p == CTLQUOTEMARK) {
5306 inquotes = ~inquotes;
5307 p++;
5308 notescaped = globbing;
5309 continue;
5310 }
5311 if (*p == '\\') {
5312 /* naked back slash */
5313 notescaped = 0;
5314 goto copy;
5315 }
5316 if (*p == CTLESC) {
5317 p++;
5318 if (notescaped && inquotes && *p != '/') {
5319 *q++ = '\\';
5320 }
5321 }
5322 notescaped = globbing;
5323 copy:
5324 *q++ = *p++;
5325 }
5326 *q = '\0';
5327 if (flag & RMESCAPE_GROW) {
5328 expdest = r;
5329 STADJUST(q - r + 1, expdest);
5330 }
5331 return r;
5332}
5333#define rmescapes(p) _rmescapes((p), 0)
5334
5335#define pmatch(a, b) !fnmatch((a), (b), 0)
5336
5337/*
5338 * Prepare a pattern for a expmeta (internal glob(3)) call.
5339 *
5340 * Returns an stalloced string.
5341 */
5342static char *
5343preglob(const char *pattern, int quoted, int flag)
5344{
5345 flag |= RMESCAPE_GLOB;
5346 if (quoted) {
5347 flag |= RMESCAPE_QUOTED;
5348 }
5349 return _rmescapes((char *)pattern, flag);
5350}
5351
5352/*
5353 * Put a string on the stack.
5354 */
5355static void
5356memtodest(const char *p, size_t len, int syntax, int quotes)
5357{
5358 char *q = expdest;
5359
5360 q = makestrspace(len * 2, q);
5361
5362 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005363 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005364 if (!c)
5365 continue;
5366 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5367 USTPUTC(CTLESC, q);
5368 USTPUTC(c, q);
5369 }
5370
5371 expdest = q;
5372}
5373
5374static void
5375strtodest(const char *p, int syntax, int quotes)
5376{
5377 memtodest(p, strlen(p), syntax, quotes);
5378}
5379
5380/*
5381 * Record the fact that we have to scan this region of the
5382 * string for IFS characters.
5383 */
5384static void
5385recordregion(int start, int end, int nulonly)
5386{
5387 struct ifsregion *ifsp;
5388
5389 if (ifslastp == NULL) {
5390 ifsp = &ifsfirst;
5391 } else {
5392 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005393 ifsp = ckzalloc(sizeof(*ifsp));
5394 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005395 ifslastp->next = ifsp;
5396 INT_ON;
5397 }
5398 ifslastp = ifsp;
5399 ifslastp->begoff = start;
5400 ifslastp->endoff = end;
5401 ifslastp->nulonly = nulonly;
5402}
5403
5404static void
5405removerecordregions(int endoff)
5406{
5407 if (ifslastp == NULL)
5408 return;
5409
5410 if (ifsfirst.endoff > endoff) {
5411 while (ifsfirst.next != NULL) {
5412 struct ifsregion *ifsp;
5413 INT_OFF;
5414 ifsp = ifsfirst.next->next;
5415 free(ifsfirst.next);
5416 ifsfirst.next = ifsp;
5417 INT_ON;
5418 }
5419 if (ifsfirst.begoff > endoff)
5420 ifslastp = NULL;
5421 else {
5422 ifslastp = &ifsfirst;
5423 ifsfirst.endoff = endoff;
5424 }
5425 return;
5426 }
5427
5428 ifslastp = &ifsfirst;
5429 while (ifslastp->next && ifslastp->next->begoff < endoff)
5430 ifslastp=ifslastp->next;
5431 while (ifslastp->next != NULL) {
5432 struct ifsregion *ifsp;
5433 INT_OFF;
5434 ifsp = ifslastp->next->next;
5435 free(ifslastp->next);
5436 ifslastp->next = ifsp;
5437 INT_ON;
5438 }
5439 if (ifslastp->endoff > endoff)
5440 ifslastp->endoff = endoff;
5441}
5442
5443static char *
5444exptilde(char *startp, char *p, int flag)
5445{
5446 char c;
5447 char *name;
5448 struct passwd *pw;
5449 const char *home;
5450 int quotes = flag & (EXP_FULL | EXP_CASE);
5451 int startloc;
5452
5453 name = p + 1;
5454
5455 while ((c = *++p) != '\0') {
5456 switch (c) {
5457 case CTLESC:
5458 return startp;
5459 case CTLQUOTEMARK:
5460 return startp;
5461 case ':':
5462 if (flag & EXP_VARTILDE)
5463 goto done;
5464 break;
5465 case '/':
5466 case CTLENDVAR:
5467 goto done;
5468 }
5469 }
5470 done:
5471 *p = '\0';
5472 if (*name == '\0') {
5473 home = lookupvar(homestr);
5474 } else {
5475 pw = getpwnam(name);
5476 if (pw == NULL)
5477 goto lose;
5478 home = pw->pw_dir;
5479 }
5480 if (!home || !*home)
5481 goto lose;
5482 *p = c;
5483 startloc = expdest - (char *)stackblock();
5484 strtodest(home, SQSYNTAX, quotes);
5485 recordregion(startloc, expdest - (char *)stackblock(), 0);
5486 return p;
5487 lose:
5488 *p = c;
5489 return startp;
5490}
5491
5492/*
5493 * Execute a command inside back quotes. If it's a builtin command, we
5494 * want to save its output in a block obtained from malloc. Otherwise
5495 * we fork off a subprocess and get the output of the command via a pipe.
5496 * Should be called with interrupts off.
5497 */
5498struct backcmd { /* result of evalbackcmd */
5499 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005500 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005501 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005502 struct job *jp; /* job structure for command */
5503};
5504
5505/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005506static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005507#define EV_EXIT 01 /* exit after evaluating tree */
5508static void evaltree(union node *, int);
5509
5510static void
5511evalbackcmd(union node *n, struct backcmd *result)
5512{
5513 int saveherefd;
5514
5515 result->fd = -1;
5516 result->buf = NULL;
5517 result->nleft = 0;
5518 result->jp = NULL;
5519 if (n == NULL) {
5520 goto out;
5521 }
5522
5523 saveherefd = herefd;
5524 herefd = -1;
5525
5526 {
5527 int pip[2];
5528 struct job *jp;
5529
5530 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005531 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005532 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005533 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5534 FORCE_INT_ON;
5535 close(pip[0]);
5536 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005537 /*close(1);*/
5538 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005539 close(pip[1]);
5540 }
5541 eflag = 0;
5542 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5543 /* NOTREACHED */
5544 }
5545 close(pip[1]);
5546 result->fd = pip[0];
5547 result->jp = jp;
5548 }
5549 herefd = saveherefd;
5550 out:
5551 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5552 result->fd, result->buf, result->nleft, result->jp));
5553}
5554
5555/*
5556 * Expand stuff in backwards quotes.
5557 */
5558static void
5559expbackq(union node *cmd, int quoted, int quotes)
5560{
5561 struct backcmd in;
5562 int i;
5563 char buf[128];
5564 char *p;
5565 char *dest;
5566 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005567 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005568 struct stackmark smark;
5569
5570 INT_OFF;
5571 setstackmark(&smark);
5572 dest = expdest;
5573 startloc = dest - (char *)stackblock();
5574 grabstackstr(dest);
5575 evalbackcmd(cmd, &in);
5576 popstackmark(&smark);
5577
5578 p = in.buf;
5579 i = in.nleft;
5580 if (i == 0)
5581 goto read;
5582 for (;;) {
5583 memtodest(p, i, syntax, quotes);
5584 read:
5585 if (in.fd < 0)
5586 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005587 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005588 TRACE(("expbackq: read returns %d\n", i));
5589 if (i <= 0)
5590 break;
5591 p = buf;
5592 }
5593
Denis Vlasenko60818682007-09-28 22:07:23 +00005594 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005595 if (in.fd >= 0) {
5596 close(in.fd);
5597 back_exitstatus = waitforjob(in.jp);
5598 }
5599 INT_ON;
5600
5601 /* Eat all trailing newlines */
5602 dest = expdest;
5603 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5604 STUNPUTC(dest);
5605 expdest = dest;
5606
5607 if (quoted == 0)
5608 recordregion(startloc, dest - (char *)stackblock(), 0);
5609 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5610 (dest - (char *)stackblock()) - startloc,
5611 (dest - (char *)stackblock()) - startloc,
5612 stackblock() + startloc));
5613}
5614
5615#if ENABLE_ASH_MATH_SUPPORT
5616/*
5617 * Expand arithmetic expression. Backup to start of expression,
5618 * evaluate, place result in (backed up) result, adjust string position.
5619 */
5620static void
5621expari(int quotes)
5622{
5623 char *p, *start;
5624 int begoff;
5625 int flag;
5626 int len;
5627
5628 /* ifsfree(); */
5629
5630 /*
5631 * This routine is slightly over-complicated for
5632 * efficiency. Next we scan backwards looking for the
5633 * start of arithmetic.
5634 */
5635 start = stackblock();
5636 p = expdest - 1;
5637 *p = '\0';
5638 p--;
5639 do {
5640 int esc;
5641
5642 while (*p != CTLARI) {
5643 p--;
5644#if DEBUG
5645 if (p < start) {
5646 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5647 }
5648#endif
5649 }
5650
5651 esc = esclen(start, p);
5652 if (!(esc % 2)) {
5653 break;
5654 }
5655
5656 p -= esc + 1;
5657 } while (1);
5658
5659 begoff = p - start;
5660
5661 removerecordregions(begoff);
5662
5663 flag = p[1];
5664
5665 expdest = p;
5666
5667 if (quotes)
5668 rmescapes(p + 2);
5669
5670 len = cvtnum(dash_arith(p + 2));
5671
5672 if (flag != '"')
5673 recordregion(begoff, begoff + len, 0);
5674}
5675#endif
5676
5677/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005678static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005679
5680/*
5681 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5682 * characters to allow for further processing. Otherwise treat
5683 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005684 *
5685 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5686 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5687 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005688 */
5689static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005690argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005691{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005692 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005693 '=',
5694 ':',
5695 CTLQUOTEMARK,
5696 CTLENDVAR,
5697 CTLESC,
5698 CTLVAR,
5699 CTLBACKQ,
5700 CTLBACKQ | CTLQUOTE,
5701#if ENABLE_ASH_MATH_SUPPORT
5702 CTLENDARI,
5703#endif
5704 0
5705 };
5706 const char *reject = spclchars;
5707 int c;
5708 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5709 int breakall = flag & EXP_WORD;
5710 int inquotes;
5711 size_t length;
5712 int startloc;
5713
5714 if (!(flag & EXP_VARTILDE)) {
5715 reject += 2;
5716 } else if (flag & EXP_VARTILDE2) {
5717 reject++;
5718 }
5719 inquotes = 0;
5720 length = 0;
5721 if (flag & EXP_TILDE) {
5722 char *q;
5723
5724 flag &= ~EXP_TILDE;
5725 tilde:
5726 q = p;
5727 if (*q == CTLESC && (flag & EXP_QWORD))
5728 q++;
5729 if (*q == '~')
5730 p = exptilde(p, q, flag);
5731 }
5732 start:
5733 startloc = expdest - (char *)stackblock();
5734 for (;;) {
5735 length += strcspn(p + length, reject);
5736 c = p[length];
5737 if (c && (!(c & 0x80)
5738#if ENABLE_ASH_MATH_SUPPORT
5739 || c == CTLENDARI
5740#endif
5741 )) {
5742 /* c == '=' || c == ':' || c == CTLENDARI */
5743 length++;
5744 }
5745 if (length > 0) {
5746 int newloc;
5747 expdest = stack_nputstr(p, length, expdest);
5748 newloc = expdest - (char *)stackblock();
5749 if (breakall && !inquotes && newloc > startloc) {
5750 recordregion(startloc, newloc, 0);
5751 }
5752 startloc = newloc;
5753 }
5754 p += length + 1;
5755 length = 0;
5756
5757 switch (c) {
5758 case '\0':
5759 goto breakloop;
5760 case '=':
5761 if (flag & EXP_VARTILDE2) {
5762 p--;
5763 continue;
5764 }
5765 flag |= EXP_VARTILDE2;
5766 reject++;
5767 /* fall through */
5768 case ':':
5769 /*
5770 * sort of a hack - expand tildes in variable
5771 * assignments (after the first '=' and after ':'s).
5772 */
5773 if (*--p == '~') {
5774 goto tilde;
5775 }
5776 continue;
5777 }
5778
5779 switch (c) {
5780 case CTLENDVAR: /* ??? */
5781 goto breakloop;
5782 case CTLQUOTEMARK:
5783 /* "$@" syntax adherence hack */
5784 if (
5785 !inquotes &&
5786 !memcmp(p, dolatstr, 4) &&
5787 (p[4] == CTLQUOTEMARK || (
5788 p[4] == CTLENDVAR &&
5789 p[5] == CTLQUOTEMARK
5790 ))
5791 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005792 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005793 goto start;
5794 }
5795 inquotes = !inquotes;
5796 addquote:
5797 if (quotes) {
5798 p--;
5799 length++;
5800 startloc++;
5801 }
5802 break;
5803 case CTLESC:
5804 startloc++;
5805 length++;
5806 goto addquote;
5807 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005808 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005809 goto start;
5810 case CTLBACKQ:
5811 c = 0;
5812 case CTLBACKQ|CTLQUOTE:
5813 expbackq(argbackq->n, c, quotes);
5814 argbackq = argbackq->next;
5815 goto start;
5816#if ENABLE_ASH_MATH_SUPPORT
5817 case CTLENDARI:
5818 p--;
5819 expari(quotes);
5820 goto start;
5821#endif
5822 }
5823 }
5824 breakloop:
5825 ;
5826}
5827
5828static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005829scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005830 int zero)
5831{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005832// This commented out code was added by James Simmons <jsimmons@infradead.org>
5833// as part of a larger change when he added support for ${var/a/b}.
5834// However, it broke # and % operators:
5835//
5836//var=ababcdcd
5837// ok bad
5838//echo ${var#ab} abcdcd abcdcd
5839//echo ${var##ab} abcdcd abcdcd
5840//echo ${var#a*b} abcdcd ababcdcd (!)
5841//echo ${var##a*b} cdcd cdcd
5842//echo ${var#?} babcdcd ababcdcd (!)
5843//echo ${var##?} babcdcd babcdcd
5844//echo ${var#*} ababcdcd babcdcd (!)
5845//echo ${var##*}
5846//echo ${var%cd} ababcd ababcd
5847//echo ${var%%cd} ababcd abab (!)
5848//echo ${var%c*d} ababcd ababcd
5849//echo ${var%%c*d} abab ababcdcd (!)
5850//echo ${var%?} ababcdc ababcdc
5851//echo ${var%%?} ababcdc ababcdcd (!)
5852//echo ${var%*} ababcdcd ababcdcd
5853//echo ${var%%*}
5854//
5855// Commenting it back out helped. Remove it completely if it really
5856// is not needed.
5857
5858 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005859 char c;
5860
5861 loc = startp;
5862 loc2 = rmesc;
5863 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005864 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005865 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005866
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005867 c = *loc2;
5868 if (zero) {
5869 *loc2 = '\0';
5870 s = rmesc;
5871 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005872 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005873
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005874// // chop off end if its '*'
5875// full = strrchr(str, '*');
5876// if (full && full != str)
5877// match--;
5878//
5879// // If str starts with '*' replace with s.
5880// if ((*str == '*') && strlen(s) >= match) {
5881// full = xstrdup(s);
5882// strncpy(full+strlen(s)-match+1, str+1, match-1);
5883// } else
5884// full = xstrndup(str, match);
5885// match = strncmp(s, full, strlen(full));
5886// free(full);
5887//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005888 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005889 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005890 return loc;
5891 if (quotes && *loc == CTLESC)
5892 loc++;
5893 loc++;
5894 loc2++;
5895 } while (c);
5896 return 0;
5897}
5898
5899static char *
5900scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5901 int zero)
5902{
5903 int esc = 0;
5904 char *loc;
5905 char *loc2;
5906
5907 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5908 int match;
5909 char c = *loc2;
5910 const char *s = loc2;
5911 if (zero) {
5912 *loc2 = '\0';
5913 s = rmesc;
5914 }
5915 match = pmatch(str, s);
5916 *loc2 = c;
5917 if (match)
5918 return loc;
5919 loc--;
5920 if (quotes) {
5921 if (--esc < 0) {
5922 esc = esclen(startp, loc);
5923 }
5924 if (esc % 2) {
5925 esc--;
5926 loc--;
5927 }
5928 }
5929 }
5930 return 0;
5931}
5932
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005933static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005934static void
5935varunset(const char *end, const char *var, const char *umsg, int varflags)
5936{
5937 const char *msg;
5938 const char *tail;
5939
5940 tail = nullstr;
5941 msg = "parameter not set";
5942 if (umsg) {
5943 if (*end == CTLENDVAR) {
5944 if (varflags & VSNUL)
5945 tail = " or null";
5946 } else
5947 msg = umsg;
5948 }
5949 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5950}
5951
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005952#if ENABLE_ASH_BASH_COMPAT
5953static char *
5954parse_sub_pattern(char *arg, int inquotes)
5955{
5956 char *idx, *repl = NULL;
5957 unsigned char c;
5958
Denis Vlasenko2659c632008-06-14 06:04:59 +00005959 idx = arg;
5960 while (1) {
5961 c = *arg;
5962 if (!c)
5963 break;
5964 if (c == '/') {
5965 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005966 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005967 repl = idx + 1;
5968 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005969 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005970 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005971 *idx++ = c;
5972 if (!inquotes && c == '\\' && arg[1] == '\\')
5973 arg++; /* skip both \\, not just first one */
5974 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005975 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005976 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005977
5978 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005979}
5980#endif /* ENABLE_ASH_BASH_COMPAT */
5981
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005982static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005983subevalvar(char *p, char *str, int strloc, int subtype,
5984 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005985{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005986 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005987 char *startp;
5988 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005989 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005990 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5991 USE_ASH_BASH_COMPAT(char null = '\0';)
5992 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5993 int saveherefd = herefd;
5994 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005995 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005996 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005997
5998 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005999 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6000 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006001 STPUTC('\0', expdest);
6002 herefd = saveherefd;
6003 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006004 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006005
6006 switch (subtype) {
6007 case VSASSIGN:
6008 setvar(str, startp, 0);
6009 amount = startp - expdest;
6010 STADJUST(amount, expdest);
6011 return startp;
6012
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006013#if ENABLE_ASH_BASH_COMPAT
6014 case VSSUBSTR:
6015 loc = str = stackblock() + strloc;
6016// TODO: number() instead? It does error checking...
6017 pos = atoi(loc);
6018 len = str - startp - 1;
6019
6020 /* *loc != '\0', guaranteed by parser */
6021 if (quotes) {
6022 char *ptr;
6023
6024 /* We must adjust the length by the number of escapes we find. */
6025 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006026 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006027 len--;
6028 ptr++;
6029 }
6030 }
6031 }
6032 orig_len = len;
6033
6034 if (*loc++ == ':') {
6035// TODO: number() instead? It does error checking...
6036 len = atoi(loc);
6037 } else {
6038 len = orig_len;
6039 while (*loc && *loc != ':')
6040 loc++;
6041 if (*loc++ == ':')
6042// TODO: number() instead? It does error checking...
6043 len = atoi(loc);
6044 }
6045 if (pos >= orig_len) {
6046 pos = 0;
6047 len = 0;
6048 }
6049 if (len > (orig_len - pos))
6050 len = orig_len - pos;
6051
6052 for (str = startp; pos; str++, pos--) {
6053 if (quotes && *str == CTLESC)
6054 str++;
6055 }
6056 for (loc = startp; len; len--) {
6057 if (quotes && *str == CTLESC)
6058 *loc++ = *str++;
6059 *loc++ = *str++;
6060 }
6061 *loc = '\0';
6062 amount = loc - expdest;
6063 STADJUST(amount, expdest);
6064 return loc;
6065#endif
6066
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006067 case VSQUESTION:
6068 varunset(p, str, startp, varflags);
6069 /* NOTREACHED */
6070 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006071 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006072
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006073 /* We'll comeback here if we grow the stack while handling
6074 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6075 * stack will need rebasing, and we'll need to remove our work
6076 * areas each time
6077 */
6078 USE_ASH_BASH_COMPAT(restart:)
6079
6080 amount = expdest - ((char *)stackblock() + resetloc);
6081 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006082 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006083
6084 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006085 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006086 if (quotes) {
6087 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6088 if (rmesc != startp) {
6089 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006090 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006091 }
6092 }
6093 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006094 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006095 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006096 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006097
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006098#if ENABLE_ASH_BASH_COMPAT
6099 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6100 char *idx, *end, *restart_detect;
6101
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006102 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006103 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6104 if (!repl)
6105 repl = &null;
6106 }
6107
6108 /* If there's no pattern to match, return the expansion unmolested */
6109 if (*str == '\0')
6110 return 0;
6111
6112 len = 0;
6113 idx = startp;
6114 end = str - 1;
6115 while (idx < end) {
6116 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6117 if (!loc) {
6118 /* No match, advance */
6119 restart_detect = stackblock();
6120 STPUTC(*idx, expdest);
6121 if (quotes && *idx == CTLESC) {
6122 idx++;
6123 len++;
6124 STPUTC(*idx, expdest);
6125 }
6126 if (stackblock() != restart_detect)
6127 goto restart;
6128 idx++;
6129 len++;
6130 rmesc++;
6131 continue;
6132 }
6133
6134 if (subtype == VSREPLACEALL) {
6135 while (idx < loc) {
6136 if (quotes && *idx == CTLESC)
6137 idx++;
6138 idx++;
6139 rmesc++;
6140 }
6141 } else
6142 idx = loc;
6143
6144 for (loc = repl; *loc; loc++) {
6145 restart_detect = stackblock();
6146 STPUTC(*loc, expdest);
6147 if (stackblock() != restart_detect)
6148 goto restart;
6149 len++;
6150 }
6151
6152 if (subtype == VSREPLACE) {
6153 while (*idx) {
6154 restart_detect = stackblock();
6155 STPUTC(*idx, expdest);
6156 if (stackblock() != restart_detect)
6157 goto restart;
6158 len++;
6159 idx++;
6160 }
6161 break;
6162 }
6163 }
6164
6165 /* We've put the replaced text into a buffer at workloc, now
6166 * move it to the right place and adjust the stack.
6167 */
6168 startp = stackblock() + startloc;
6169 STPUTC('\0', expdest);
6170 memmove(startp, stackblock() + workloc, len);
6171 startp[len++] = '\0';
6172 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6173 STADJUST(-amount, expdest);
6174 return startp;
6175 }
6176#endif /* ENABLE_ASH_BASH_COMPAT */
6177
6178 subtype -= VSTRIMRIGHT;
6179#if DEBUG
6180 if (subtype < 0 || subtype > 7)
6181 abort();
6182#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006183 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6184 zero = subtype >> 1;
6185 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6186 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6187
6188 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6189 if (loc) {
6190 if (zero) {
6191 memmove(startp, loc, str - loc);
6192 loc = startp + (str - loc) - 1;
6193 }
6194 *loc = '\0';
6195 amount = loc - expdest;
6196 STADJUST(amount, expdest);
6197 }
6198 return loc;
6199}
6200
6201/*
6202 * Add the value of a specialized variable to the stack string.
6203 */
6204static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006205varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006206{
6207 int num;
6208 char *p;
6209 int i;
6210 int sep = 0;
6211 int sepq = 0;
6212 ssize_t len = 0;
6213 char **ap;
6214 int syntax;
6215 int quoted = varflags & VSQUOTE;
6216 int subtype = varflags & VSTYPE;
6217 int quotes = flags & (EXP_FULL | EXP_CASE);
6218
6219 if (quoted && (flags & EXP_FULL))
6220 sep = 1 << CHAR_BIT;
6221
6222 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6223 switch (*name) {
6224 case '$':
6225 num = rootpid;
6226 goto numvar;
6227 case '?':
6228 num = exitstatus;
6229 goto numvar;
6230 case '#':
6231 num = shellparam.nparam;
6232 goto numvar;
6233 case '!':
6234 num = backgndpid;
6235 if (num == 0)
6236 return -1;
6237 numvar:
6238 len = cvtnum(num);
6239 break;
6240 case '-':
6241 p = makestrspace(NOPTS, expdest);
6242 for (i = NOPTS - 1; i >= 0; i--) {
6243 if (optlist[i]) {
6244 USTPUTC(optletters(i), p);
6245 len++;
6246 }
6247 }
6248 expdest = p;
6249 break;
6250 case '@':
6251 if (sep)
6252 goto param;
6253 /* fall through */
6254 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006255 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006256 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6257 sepq = 1;
6258 param:
6259 ap = shellparam.p;
6260 if (!ap)
6261 return -1;
6262 while ((p = *ap++)) {
6263 size_t partlen;
6264
6265 partlen = strlen(p);
6266 len += partlen;
6267
6268 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6269 memtodest(p, partlen, syntax, quotes);
6270
6271 if (*ap && sep) {
6272 char *q;
6273
6274 len++;
6275 if (subtype == VSPLUS || subtype == VSLENGTH) {
6276 continue;
6277 }
6278 q = expdest;
6279 if (sepq)
6280 STPUTC(CTLESC, q);
6281 STPUTC(sep, q);
6282 expdest = q;
6283 }
6284 }
6285 return len;
6286 case '0':
6287 case '1':
6288 case '2':
6289 case '3':
6290 case '4':
6291 case '5':
6292 case '6':
6293 case '7':
6294 case '8':
6295 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006296// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006297 num = atoi(name);
6298 if (num < 0 || num > shellparam.nparam)
6299 return -1;
6300 p = num ? shellparam.p[num - 1] : arg0;
6301 goto value;
6302 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006303 /* NB: name has form "VAR=..." */
6304
6305 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6306 * which should be considered before we check variables. */
6307 if (var_str_list) {
6308 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6309 p = NULL;
6310 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006311 char *str, *eq;
6312 str = var_str_list->text;
6313 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006314 if (!eq) /* stop at first non-assignment */
6315 break;
6316 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006317 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006318 && strncmp(str, name, name_len) == 0) {
6319 p = eq;
6320 /* goto value; - WRONG! */
6321 /* think "A=1 A=2 B=$A" */
6322 }
6323 var_str_list = var_str_list->next;
6324 } while (var_str_list);
6325 if (p)
6326 goto value;
6327 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006328 p = lookupvar(name);
6329 value:
6330 if (!p)
6331 return -1;
6332
6333 len = strlen(p);
6334 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6335 memtodest(p, len, syntax, quotes);
6336 return len;
6337 }
6338
6339 if (subtype == VSPLUS || subtype == VSLENGTH)
6340 STADJUST(-len, expdest);
6341 return len;
6342}
6343
6344/*
6345 * Expand a variable, and return a pointer to the next character in the
6346 * input string.
6347 */
6348static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006349evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006350{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006351 char varflags;
6352 char subtype;
6353 char quoted;
6354 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006355 char *var;
6356 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006357 int startloc;
6358 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006359
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006360 varflags = *p++;
6361 subtype = varflags & VSTYPE;
6362 quoted = varflags & VSQUOTE;
6363 var = p;
6364 easy = (!quoted || (*var == '@' && shellparam.nparam));
6365 startloc = expdest - (char *)stackblock();
6366 p = strchr(p, '=') + 1;
6367
6368 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006369 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006370 if (varflags & VSNUL)
6371 varlen--;
6372
6373 if (subtype == VSPLUS) {
6374 varlen = -1 - varlen;
6375 goto vsplus;
6376 }
6377
6378 if (subtype == VSMINUS) {
6379 vsplus:
6380 if (varlen < 0) {
6381 argstr(
6382 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006383 (quoted ? EXP_QWORD : EXP_WORD),
6384 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006385 );
6386 goto end;
6387 }
6388 if (easy)
6389 goto record;
6390 goto end;
6391 }
6392
6393 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6394 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006395 if (subevalvar(p, var, /* strloc: */ 0,
6396 subtype, startloc, varflags,
6397 /* quotes: */ 0,
6398 var_str_list)
6399 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006400 varflags &= ~VSNUL;
6401 /*
6402 * Remove any recorded regions beyond
6403 * start of variable
6404 */
6405 removerecordregions(startloc);
6406 goto again;
6407 }
6408 goto end;
6409 }
6410 if (easy)
6411 goto record;
6412 goto end;
6413 }
6414
6415 if (varlen < 0 && uflag)
6416 varunset(p, var, 0, 0);
6417
6418 if (subtype == VSLENGTH) {
6419 cvtnum(varlen > 0 ? varlen : 0);
6420 goto record;
6421 }
6422
6423 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006424 if (easy)
6425 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006426 goto end;
6427 }
6428
6429#if DEBUG
6430 switch (subtype) {
6431 case VSTRIMLEFT:
6432 case VSTRIMLEFTMAX:
6433 case VSTRIMRIGHT:
6434 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006435#if ENABLE_ASH_BASH_COMPAT
6436 case VSSUBSTR:
6437 case VSREPLACE:
6438 case VSREPLACEALL:
6439#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006440 break;
6441 default:
6442 abort();
6443 }
6444#endif
6445
6446 if (varlen >= 0) {
6447 /*
6448 * Terminate the string and start recording the pattern
6449 * right after it
6450 */
6451 STPUTC('\0', expdest);
6452 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006453 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6454 startloc, varflags,
6455 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6456 var_str_list)
6457 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006458 int amount = expdest - (
6459 (char *)stackblock() + patloc - 1
6460 );
6461 STADJUST(-amount, expdest);
6462 }
6463 /* Remove any recorded regions beyond start of variable */
6464 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006465 record:
6466 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006467 }
6468
6469 end:
6470 if (subtype != VSNORMAL) { /* skip to end of alternative */
6471 int nesting = 1;
6472 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006473 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006474 if (c == CTLESC)
6475 p++;
6476 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6477 if (varlen >= 0)
6478 argbackq = argbackq->next;
6479 } else if (c == CTLVAR) {
6480 if ((*p++ & VSTYPE) != VSNORMAL)
6481 nesting++;
6482 } else if (c == CTLENDVAR) {
6483 if (--nesting == 0)
6484 break;
6485 }
6486 }
6487 }
6488 return p;
6489}
6490
6491/*
6492 * Break the argument string into pieces based upon IFS and add the
6493 * strings to the argument list. The regions of the string to be
6494 * searched for IFS characters have been stored by recordregion.
6495 */
6496static void
6497ifsbreakup(char *string, struct arglist *arglist)
6498{
6499 struct ifsregion *ifsp;
6500 struct strlist *sp;
6501 char *start;
6502 char *p;
6503 char *q;
6504 const char *ifs, *realifs;
6505 int ifsspc;
6506 int nulonly;
6507
6508 start = string;
6509 if (ifslastp != NULL) {
6510 ifsspc = 0;
6511 nulonly = 0;
6512 realifs = ifsset() ? ifsval() : defifs;
6513 ifsp = &ifsfirst;
6514 do {
6515 p = string + ifsp->begoff;
6516 nulonly = ifsp->nulonly;
6517 ifs = nulonly ? nullstr : realifs;
6518 ifsspc = 0;
6519 while (p < string + ifsp->endoff) {
6520 q = p;
6521 if (*p == CTLESC)
6522 p++;
6523 if (!strchr(ifs, *p)) {
6524 p++;
6525 continue;
6526 }
6527 if (!nulonly)
6528 ifsspc = (strchr(defifs, *p) != NULL);
6529 /* Ignore IFS whitespace at start */
6530 if (q == start && ifsspc) {
6531 p++;
6532 start = p;
6533 continue;
6534 }
6535 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006536 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006537 sp->text = start;
6538 *arglist->lastp = sp;
6539 arglist->lastp = &sp->next;
6540 p++;
6541 if (!nulonly) {
6542 for (;;) {
6543 if (p >= string + ifsp->endoff) {
6544 break;
6545 }
6546 q = p;
6547 if (*p == CTLESC)
6548 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006549 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006550 p = q;
6551 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006552 }
6553 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006554 if (ifsspc) {
6555 p++;
6556 ifsspc = 0;
6557 } else {
6558 p = q;
6559 break;
6560 }
6561 } else
6562 p++;
6563 }
6564 }
6565 start = p;
6566 } /* while */
6567 ifsp = ifsp->next;
6568 } while (ifsp != NULL);
6569 if (nulonly)
6570 goto add;
6571 }
6572
6573 if (!*start)
6574 return;
6575
6576 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006577 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006578 sp->text = start;
6579 *arglist->lastp = sp;
6580 arglist->lastp = &sp->next;
6581}
6582
6583static void
6584ifsfree(void)
6585{
6586 struct ifsregion *p;
6587
6588 INT_OFF;
6589 p = ifsfirst.next;
6590 do {
6591 struct ifsregion *ifsp;
6592 ifsp = p->next;
6593 free(p);
6594 p = ifsp;
6595 } while (p);
6596 ifslastp = NULL;
6597 ifsfirst.next = NULL;
6598 INT_ON;
6599}
6600
6601/*
6602 * Add a file name to the list.
6603 */
6604static void
6605addfname(const char *name)
6606{
6607 struct strlist *sp;
6608
Denis Vlasenko597906c2008-02-20 16:38:54 +00006609 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006610 sp->text = ststrdup(name);
6611 *exparg.lastp = sp;
6612 exparg.lastp = &sp->next;
6613}
6614
6615static char *expdir;
6616
6617/*
6618 * Do metacharacter (i.e. *, ?, [...]) expansion.
6619 */
6620static void
6621expmeta(char *enddir, char *name)
6622{
6623 char *p;
6624 const char *cp;
6625 char *start;
6626 char *endname;
6627 int metaflag;
6628 struct stat statb;
6629 DIR *dirp;
6630 struct dirent *dp;
6631 int atend;
6632 int matchdot;
6633
6634 metaflag = 0;
6635 start = name;
6636 for (p = name; *p; p++) {
6637 if (*p == '*' || *p == '?')
6638 metaflag = 1;
6639 else if (*p == '[') {
6640 char *q = p + 1;
6641 if (*q == '!')
6642 q++;
6643 for (;;) {
6644 if (*q == '\\')
6645 q++;
6646 if (*q == '/' || *q == '\0')
6647 break;
6648 if (*++q == ']') {
6649 metaflag = 1;
6650 break;
6651 }
6652 }
6653 } else if (*p == '\\')
6654 p++;
6655 else if (*p == '/') {
6656 if (metaflag)
6657 goto out;
6658 start = p + 1;
6659 }
6660 }
6661 out:
6662 if (metaflag == 0) { /* we've reached the end of the file name */
6663 if (enddir != expdir)
6664 metaflag++;
6665 p = name;
6666 do {
6667 if (*p == '\\')
6668 p++;
6669 *enddir++ = *p;
6670 } while (*p++);
6671 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6672 addfname(expdir);
6673 return;
6674 }
6675 endname = p;
6676 if (name < start) {
6677 p = name;
6678 do {
6679 if (*p == '\\')
6680 p++;
6681 *enddir++ = *p++;
6682 } while (p < start);
6683 }
6684 if (enddir == expdir) {
6685 cp = ".";
6686 } else if (enddir == expdir + 1 && *expdir == '/') {
6687 cp = "/";
6688 } else {
6689 cp = expdir;
6690 enddir[-1] = '\0';
6691 }
6692 dirp = opendir(cp);
6693 if (dirp == NULL)
6694 return;
6695 if (enddir != expdir)
6696 enddir[-1] = '/';
6697 if (*endname == 0) {
6698 atend = 1;
6699 } else {
6700 atend = 0;
6701 *endname++ = '\0';
6702 }
6703 matchdot = 0;
6704 p = start;
6705 if (*p == '\\')
6706 p++;
6707 if (*p == '.')
6708 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006709 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006710 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006711 continue;
6712 if (pmatch(start, dp->d_name)) {
6713 if (atend) {
6714 strcpy(enddir, dp->d_name);
6715 addfname(expdir);
6716 } else {
6717 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6718 continue;
6719 p[-1] = '/';
6720 expmeta(p, endname);
6721 }
6722 }
6723 }
6724 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006725 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006726 endname[-1] = '/';
6727}
6728
6729static struct strlist *
6730msort(struct strlist *list, int len)
6731{
6732 struct strlist *p, *q = NULL;
6733 struct strlist **lpp;
6734 int half;
6735 int n;
6736
6737 if (len <= 1)
6738 return list;
6739 half = len >> 1;
6740 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006741 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006742 q = p;
6743 p = p->next;
6744 }
6745 q->next = NULL; /* terminate first half of list */
6746 q = msort(list, half); /* sort first half of list */
6747 p = msort(p, len - half); /* sort second half */
6748 lpp = &list;
6749 for (;;) {
6750#if ENABLE_LOCALE_SUPPORT
6751 if (strcoll(p->text, q->text) < 0)
6752#else
6753 if (strcmp(p->text, q->text) < 0)
6754#endif
6755 {
6756 *lpp = p;
6757 lpp = &p->next;
6758 p = *lpp;
6759 if (p == NULL) {
6760 *lpp = q;
6761 break;
6762 }
6763 } else {
6764 *lpp = q;
6765 lpp = &q->next;
6766 q = *lpp;
6767 if (q == NULL) {
6768 *lpp = p;
6769 break;
6770 }
6771 }
6772 }
6773 return list;
6774}
6775
6776/*
6777 * Sort the results of file name expansion. It calculates the number of
6778 * strings to sort and then calls msort (short for merge sort) to do the
6779 * work.
6780 */
6781static struct strlist *
6782expsort(struct strlist *str)
6783{
6784 int len;
6785 struct strlist *sp;
6786
6787 len = 0;
6788 for (sp = str; sp; sp = sp->next)
6789 len++;
6790 return msort(str, len);
6791}
6792
6793static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006794expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006795{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006796 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006797 '*', '?', '[', 0
6798 };
6799 /* TODO - EXP_REDIR */
6800
6801 while (str) {
6802 struct strlist **savelastp;
6803 struct strlist *sp;
6804 char *p;
6805
6806 if (fflag)
6807 goto nometa;
6808 if (!strpbrk(str->text, metachars))
6809 goto nometa;
6810 savelastp = exparg.lastp;
6811
6812 INT_OFF;
6813 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6814 {
6815 int i = strlen(str->text);
6816 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6817 }
6818
6819 expmeta(expdir, p);
6820 free(expdir);
6821 if (p != str->text)
6822 free(p);
6823 INT_ON;
6824 if (exparg.lastp == savelastp) {
6825 /*
6826 * no matches
6827 */
6828 nometa:
6829 *exparg.lastp = str;
6830 rmescapes(str->text);
6831 exparg.lastp = &str->next;
6832 } else {
6833 *exparg.lastp = NULL;
6834 *savelastp = sp = expsort(*savelastp);
6835 while (sp->next != NULL)
6836 sp = sp->next;
6837 exparg.lastp = &sp->next;
6838 }
6839 str = str->next;
6840 }
6841}
6842
6843/*
6844 * Perform variable substitution and command substitution on an argument,
6845 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6846 * perform splitting and file name expansion. When arglist is NULL, perform
6847 * here document expansion.
6848 */
6849static void
6850expandarg(union node *arg, struct arglist *arglist, int flag)
6851{
6852 struct strlist *sp;
6853 char *p;
6854
6855 argbackq = arg->narg.backquote;
6856 STARTSTACKSTR(expdest);
6857 ifsfirst.next = NULL;
6858 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006859 argstr(arg->narg.text, flag,
6860 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006861 p = _STPUTC('\0', expdest);
6862 expdest = p - 1;
6863 if (arglist == NULL) {
6864 return; /* here document expanded */
6865 }
6866 p = grabstackstr(p);
6867 exparg.lastp = &exparg.list;
6868 /*
6869 * TODO - EXP_REDIR
6870 */
6871 if (flag & EXP_FULL) {
6872 ifsbreakup(p, &exparg);
6873 *exparg.lastp = NULL;
6874 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006875 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006876 } else {
6877 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6878 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006879 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006880 sp->text = p;
6881 *exparg.lastp = sp;
6882 exparg.lastp = &sp->next;
6883 }
6884 if (ifsfirst.next)
6885 ifsfree();
6886 *exparg.lastp = NULL;
6887 if (exparg.list) {
6888 *arglist->lastp = exparg.list;
6889 arglist->lastp = exparg.lastp;
6890 }
6891}
6892
6893/*
6894 * Expand shell variables and backquotes inside a here document.
6895 */
6896static void
6897expandhere(union node *arg, int fd)
6898{
6899 herefd = fd;
6900 expandarg(arg, (struct arglist *)NULL, 0);
6901 full_write(fd, stackblock(), expdest - (char *)stackblock());
6902}
6903
6904/*
6905 * Returns true if the pattern matches the string.
6906 */
6907static int
6908patmatch(char *pattern, const char *string)
6909{
6910 return pmatch(preglob(pattern, 0, 0), string);
6911}
6912
6913/*
6914 * See if a pattern matches in a case statement.
6915 */
6916static int
6917casematch(union node *pattern, char *val)
6918{
6919 struct stackmark smark;
6920 int result;
6921
6922 setstackmark(&smark);
6923 argbackq = pattern->narg.backquote;
6924 STARTSTACKSTR(expdest);
6925 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006926 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6927 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006928 STACKSTRNUL(expdest);
6929 result = patmatch(stackblock(), val);
6930 popstackmark(&smark);
6931 return result;
6932}
6933
6934
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006935/* ============ find_command */
6936
6937struct builtincmd {
6938 const char *name;
6939 int (*builtin)(int, char **);
6940 /* unsigned flags; */
6941};
6942#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006943/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006944 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006945#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006946#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006947
6948struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006949 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006950 union param {
6951 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006952 /* index >= 0 for commands without path (slashes) */
6953 /* (TODO: what exactly does the value mean? PATH position?) */
6954 /* index == -1 for commands with slashes */
6955 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006956 const struct builtincmd *cmd;
6957 struct funcnode *func;
6958 } u;
6959};
6960/* values of cmdtype */
6961#define CMDUNKNOWN -1 /* no entry in table for command */
6962#define CMDNORMAL 0 /* command is an executable program */
6963#define CMDFUNCTION 1 /* command is a shell function */
6964#define CMDBUILTIN 2 /* command is a shell builtin */
6965
6966/* action to find_command() */
6967#define DO_ERR 0x01 /* prints errors */
6968#define DO_ABS 0x02 /* checks absolute paths */
6969#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6970#define DO_ALTPATH 0x08 /* using alternate path */
6971#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6972
6973static void find_command(char *, struct cmdentry *, int, const char *);
6974
6975
6976/* ============ Hashing commands */
6977
6978/*
6979 * When commands are first encountered, they are entered in a hash table.
6980 * This ensures that a full path search will not have to be done for them
6981 * on each invocation.
6982 *
6983 * We should investigate converting to a linear search, even though that
6984 * would make the command name "hash" a misnomer.
6985 */
6986
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006987struct tblentry {
6988 struct tblentry *next; /* next entry in hash chain */
6989 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006990 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006991 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006992 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006993};
6994
Denis Vlasenko01631112007-12-16 17:20:38 +00006995static struct tblentry **cmdtable;
6996#define INIT_G_cmdtable() do { \
6997 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6998} while (0)
6999
7000static int builtinloc = -1; /* index in path of %builtin, or -1 */
7001
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007002
7003static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007004tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007005{
7006 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007007
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007008#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007009 if (applet_no >= 0) {
7010 if (APPLET_IS_NOEXEC(applet_no))
7011 run_applet_no_and_exit(applet_no, argv);
7012 /* re-exec ourselves with the new arguments */
7013 execve(bb_busybox_exec_path, argv, envp);
7014 /* If they called chroot or otherwise made the binary no longer
7015 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007016 }
7017#endif
7018
7019 repeat:
7020#ifdef SYSV
7021 do {
7022 execve(cmd, argv, envp);
7023 } while (errno == EINTR);
7024#else
7025 execve(cmd, argv, envp);
7026#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007027 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007028 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007029 return;
7030 }
7031 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007032 char **ap;
7033 char **new;
7034
7035 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007036 continue;
7037 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007038 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007039 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007040 ap += 2;
7041 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007042 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007043 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007044 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007045 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007046 goto repeat;
7047 }
7048}
7049
7050/*
7051 * Exec a program. Never returns. If you change this routine, you may
7052 * have to change the find_command routine as well.
7053 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007054static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007055static void
7056shellexec(char **argv, const char *path, int idx)
7057{
7058 char *cmdname;
7059 int e;
7060 char **envp;
7061 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007062#if ENABLE_FEATURE_SH_STANDALONE
7063 int applet_no = -1;
7064#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007065
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007066 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007067 envp = listvars(VEXPORT, VUNSET, 0);
7068 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007069#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007070 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007071#endif
7072 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007073 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007074 e = errno;
7075 } else {
7076 e = ENOENT;
7077 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7078 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007079 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007080 if (errno != ENOENT && errno != ENOTDIR)
7081 e = errno;
7082 }
7083 stunalloc(cmdname);
7084 }
7085 }
7086
7087 /* Map to POSIX errors */
7088 switch (e) {
7089 case EACCES:
7090 exerrno = 126;
7091 break;
7092 case ENOENT:
7093 exerrno = 127;
7094 break;
7095 default:
7096 exerrno = 2;
7097 break;
7098 }
7099 exitstatus = exerrno;
7100 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007101 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007102 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7103 /* NOTREACHED */
7104}
7105
7106static void
7107printentry(struct tblentry *cmdp)
7108{
7109 int idx;
7110 const char *path;
7111 char *name;
7112
7113 idx = cmdp->param.index;
7114 path = pathval();
7115 do {
7116 name = padvance(&path, cmdp->cmdname);
7117 stunalloc(name);
7118 } while (--idx >= 0);
7119 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7120}
7121
7122/*
7123 * Clear out command entries. The argument specifies the first entry in
7124 * PATH which has changed.
7125 */
7126static void
7127clearcmdentry(int firstchange)
7128{
7129 struct tblentry **tblp;
7130 struct tblentry **pp;
7131 struct tblentry *cmdp;
7132
7133 INT_OFF;
7134 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7135 pp = tblp;
7136 while ((cmdp = *pp) != NULL) {
7137 if ((cmdp->cmdtype == CMDNORMAL &&
7138 cmdp->param.index >= firstchange)
7139 || (cmdp->cmdtype == CMDBUILTIN &&
7140 builtinloc >= firstchange)
7141 ) {
7142 *pp = cmdp->next;
7143 free(cmdp);
7144 } else {
7145 pp = &cmdp->next;
7146 }
7147 }
7148 }
7149 INT_ON;
7150}
7151
7152/*
7153 * Locate a command in the command hash table. If "add" is nonzero,
7154 * add the command to the table if it is not already present. The
7155 * variable "lastcmdentry" is set to point to the address of the link
7156 * pointing to the entry, so that delete_cmd_entry can delete the
7157 * entry.
7158 *
7159 * Interrupts must be off if called with add != 0.
7160 */
7161static struct tblentry **lastcmdentry;
7162
7163static struct tblentry *
7164cmdlookup(const char *name, int add)
7165{
7166 unsigned int hashval;
7167 const char *p;
7168 struct tblentry *cmdp;
7169 struct tblentry **pp;
7170
7171 p = name;
7172 hashval = (unsigned char)*p << 4;
7173 while (*p)
7174 hashval += (unsigned char)*p++;
7175 hashval &= 0x7FFF;
7176 pp = &cmdtable[hashval % CMDTABLESIZE];
7177 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7178 if (strcmp(cmdp->cmdname, name) == 0)
7179 break;
7180 pp = &cmdp->next;
7181 }
7182 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007183 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7184 + strlen(name)
7185 /* + 1 - already done because
7186 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007187 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007188 cmdp->cmdtype = CMDUNKNOWN;
7189 strcpy(cmdp->cmdname, name);
7190 }
7191 lastcmdentry = pp;
7192 return cmdp;
7193}
7194
7195/*
7196 * Delete the command entry returned on the last lookup.
7197 */
7198static void
7199delete_cmd_entry(void)
7200{
7201 struct tblentry *cmdp;
7202
7203 INT_OFF;
7204 cmdp = *lastcmdentry;
7205 *lastcmdentry = cmdp->next;
7206 if (cmdp->cmdtype == CMDFUNCTION)
7207 freefunc(cmdp->param.func);
7208 free(cmdp);
7209 INT_ON;
7210}
7211
7212/*
7213 * Add a new command entry, replacing any existing command entry for
7214 * the same name - except special builtins.
7215 */
7216static void
7217addcmdentry(char *name, struct cmdentry *entry)
7218{
7219 struct tblentry *cmdp;
7220
7221 cmdp = cmdlookup(name, 1);
7222 if (cmdp->cmdtype == CMDFUNCTION) {
7223 freefunc(cmdp->param.func);
7224 }
7225 cmdp->cmdtype = entry->cmdtype;
7226 cmdp->param = entry->u;
7227 cmdp->rehash = 0;
7228}
7229
7230static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007231hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007232{
7233 struct tblentry **pp;
7234 struct tblentry *cmdp;
7235 int c;
7236 struct cmdentry entry;
7237 char *name;
7238
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007239 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007240 clearcmdentry(0);
7241 return 0;
7242 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007243
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007244 if (*argptr == NULL) {
7245 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7246 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7247 if (cmdp->cmdtype == CMDNORMAL)
7248 printentry(cmdp);
7249 }
7250 }
7251 return 0;
7252 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007253
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007254 c = 0;
7255 while ((name = *argptr) != NULL) {
7256 cmdp = cmdlookup(name, 0);
7257 if (cmdp != NULL
7258 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007259 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7260 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007261 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007262 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007263 find_command(name, &entry, DO_ERR, pathval());
7264 if (entry.cmdtype == CMDUNKNOWN)
7265 c = 1;
7266 argptr++;
7267 }
7268 return c;
7269}
7270
7271/*
7272 * Called when a cd is done. Marks all commands so the next time they
7273 * are executed they will be rehashed.
7274 */
7275static void
7276hashcd(void)
7277{
7278 struct tblentry **pp;
7279 struct tblentry *cmdp;
7280
7281 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7282 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007283 if (cmdp->cmdtype == CMDNORMAL
7284 || (cmdp->cmdtype == CMDBUILTIN
7285 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7286 && builtinloc > 0)
7287 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007288 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007289 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007290 }
7291 }
7292}
7293
7294/*
7295 * Fix command hash table when PATH changed.
7296 * Called before PATH is changed. The argument is the new value of PATH;
7297 * pathval() still returns the old value at this point.
7298 * Called with interrupts off.
7299 */
7300static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007301changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007302{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007303 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007304 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007305 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007306 int idx_bltin;
7307
7308 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007309 firstchange = 9999; /* assume no change */
7310 idx = 0;
7311 idx_bltin = -1;
7312 for (;;) {
7313 if (*old != *new) {
7314 firstchange = idx;
7315 if ((*old == '\0' && *new == ':')
7316 || (*old == ':' && *new == '\0'))
7317 firstchange++;
7318 old = new; /* ignore subsequent differences */
7319 }
7320 if (*new == '\0')
7321 break;
7322 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7323 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007324 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007325 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007326 new++, old++;
7327 }
7328 if (builtinloc < 0 && idx_bltin >= 0)
7329 builtinloc = idx_bltin; /* zap builtins */
7330 if (builtinloc >= 0 && idx_bltin < 0)
7331 firstchange = 0;
7332 clearcmdentry(firstchange);
7333 builtinloc = idx_bltin;
7334}
7335
7336#define TEOF 0
7337#define TNL 1
7338#define TREDIR 2
7339#define TWORD 3
7340#define TSEMI 4
7341#define TBACKGND 5
7342#define TAND 6
7343#define TOR 7
7344#define TPIPE 8
7345#define TLP 9
7346#define TRP 10
7347#define TENDCASE 11
7348#define TENDBQUOTE 12
7349#define TNOT 13
7350#define TCASE 14
7351#define TDO 15
7352#define TDONE 16
7353#define TELIF 17
7354#define TELSE 18
7355#define TESAC 19
7356#define TFI 20
7357#define TFOR 21
7358#define TIF 22
7359#define TIN 23
7360#define TTHEN 24
7361#define TUNTIL 25
7362#define TWHILE 26
7363#define TBEGIN 27
7364#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007365typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007366
7367/* first char is indicating which tokens mark the end of a list */
7368static const char *const tokname_array[] = {
7369 "\1end of file",
7370 "\0newline",
7371 "\0redirection",
7372 "\0word",
7373 "\0;",
7374 "\0&",
7375 "\0&&",
7376 "\0||",
7377 "\0|",
7378 "\0(",
7379 "\1)",
7380 "\1;;",
7381 "\1`",
7382#define KWDOFFSET 13
7383 /* the following are keywords */
7384 "\0!",
7385 "\0case",
7386 "\1do",
7387 "\1done",
7388 "\1elif",
7389 "\1else",
7390 "\1esac",
7391 "\1fi",
7392 "\0for",
7393 "\0if",
7394 "\0in",
7395 "\1then",
7396 "\0until",
7397 "\0while",
7398 "\0{",
7399 "\1}",
7400};
7401
7402static const char *
7403tokname(int tok)
7404{
7405 static char buf[16];
7406
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007407//try this:
7408//if (tok < TSEMI) return tokname_array[tok] + 1;
7409//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7410//return buf;
7411
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007412 if (tok >= TSEMI)
7413 buf[0] = '"';
7414 sprintf(buf + (tok >= TSEMI), "%s%c",
7415 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7416 return buf;
7417}
7418
7419/* Wrapper around strcmp for qsort/bsearch/... */
7420static int
7421pstrcmp(const void *a, const void *b)
7422{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007423 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007424}
7425
7426static const char *const *
7427findkwd(const char *s)
7428{
7429 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007430 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7431 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007432}
7433
7434/*
7435 * Locate and print what a word is...
7436 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007437static int
7438describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007439{
7440 struct cmdentry entry;
7441 struct tblentry *cmdp;
7442#if ENABLE_ASH_ALIAS
7443 const struct alias *ap;
7444#endif
7445 const char *path = pathval();
7446
7447 if (describe_command_verbose) {
7448 out1str(command);
7449 }
7450
7451 /* First look at the keywords */
7452 if (findkwd(command)) {
7453 out1str(describe_command_verbose ? " is a shell keyword" : command);
7454 goto out;
7455 }
7456
7457#if ENABLE_ASH_ALIAS
7458 /* Then look at the aliases */
7459 ap = lookupalias(command, 0);
7460 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007461 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007462 out1str("alias ");
7463 printalias(ap);
7464 return 0;
7465 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007466 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007467 goto out;
7468 }
7469#endif
7470 /* Then check if it is a tracked alias */
7471 cmdp = cmdlookup(command, 0);
7472 if (cmdp != NULL) {
7473 entry.cmdtype = cmdp->cmdtype;
7474 entry.u = cmdp->param;
7475 } else {
7476 /* Finally use brute force */
7477 find_command(command, &entry, DO_ABS, path);
7478 }
7479
7480 switch (entry.cmdtype) {
7481 case CMDNORMAL: {
7482 int j = entry.u.index;
7483 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007484 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007485 p = command;
7486 } else {
7487 do {
7488 p = padvance(&path, command);
7489 stunalloc(p);
7490 } while (--j >= 0);
7491 }
7492 if (describe_command_verbose) {
7493 out1fmt(" is%s %s",
7494 (cmdp ? " a tracked alias for" : nullstr), p
7495 );
7496 } else {
7497 out1str(p);
7498 }
7499 break;
7500 }
7501
7502 case CMDFUNCTION:
7503 if (describe_command_verbose) {
7504 out1str(" is a shell function");
7505 } else {
7506 out1str(command);
7507 }
7508 break;
7509
7510 case CMDBUILTIN:
7511 if (describe_command_verbose) {
7512 out1fmt(" is a %sshell builtin",
7513 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7514 "special " : nullstr
7515 );
7516 } else {
7517 out1str(command);
7518 }
7519 break;
7520
7521 default:
7522 if (describe_command_verbose) {
7523 out1str(": not found\n");
7524 }
7525 return 127;
7526 }
7527 out:
7528 outstr("\n", stdout);
7529 return 0;
7530}
7531
7532static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007533typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007534{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007535 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007536 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007537 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007538
Denis Vlasenko46846e22007-05-20 13:08:31 +00007539 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007540 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007541 i++;
7542 verbose = 0;
7543 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007544 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007545 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007546 }
7547 return err;
7548}
7549
7550#if ENABLE_ASH_CMDCMD
7551static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007552commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007553{
7554 int c;
7555 enum {
7556 VERIFY_BRIEF = 1,
7557 VERIFY_VERBOSE = 2,
7558 } verify = 0;
7559
7560 while ((c = nextopt("pvV")) != '\0')
7561 if (c == 'V')
7562 verify |= VERIFY_VERBOSE;
7563 else if (c == 'v')
7564 verify |= VERIFY_BRIEF;
7565#if DEBUG
7566 else if (c != 'p')
7567 abort();
7568#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007569 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7570 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007571 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007572 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007573
7574 return 0;
7575}
7576#endif
7577
7578
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007579/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007580
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007581static int funcblocksize; /* size of structures in function */
7582static int funcstringsize; /* size of strings in node */
7583static void *funcblock; /* block to allocate function from */
7584static char *funcstring; /* block to allocate strings from */
7585
Eric Andersencb57d552001-06-28 07:25:16 +00007586/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007587#define EV_EXIT 01 /* exit after evaluating tree */
7588#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7589#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007590
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007591static const short nodesize[26] = {
7592 SHELL_ALIGN(sizeof(struct ncmd)),
7593 SHELL_ALIGN(sizeof(struct npipe)),
7594 SHELL_ALIGN(sizeof(struct nredir)),
7595 SHELL_ALIGN(sizeof(struct nredir)),
7596 SHELL_ALIGN(sizeof(struct nredir)),
7597 SHELL_ALIGN(sizeof(struct nbinary)),
7598 SHELL_ALIGN(sizeof(struct nbinary)),
7599 SHELL_ALIGN(sizeof(struct nbinary)),
7600 SHELL_ALIGN(sizeof(struct nif)),
7601 SHELL_ALIGN(sizeof(struct nbinary)),
7602 SHELL_ALIGN(sizeof(struct nbinary)),
7603 SHELL_ALIGN(sizeof(struct nfor)),
7604 SHELL_ALIGN(sizeof(struct ncase)),
7605 SHELL_ALIGN(sizeof(struct nclist)),
7606 SHELL_ALIGN(sizeof(struct narg)),
7607 SHELL_ALIGN(sizeof(struct narg)),
7608 SHELL_ALIGN(sizeof(struct nfile)),
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 ndup)),
7614 SHELL_ALIGN(sizeof(struct ndup)),
7615 SHELL_ALIGN(sizeof(struct nhere)),
7616 SHELL_ALIGN(sizeof(struct nhere)),
7617 SHELL_ALIGN(sizeof(struct nnot)),
7618};
7619
7620static void calcsize(union node *n);
7621
7622static void
7623sizenodelist(struct nodelist *lp)
7624{
7625 while (lp) {
7626 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7627 calcsize(lp->n);
7628 lp = lp->next;
7629 }
7630}
7631
7632static void
7633calcsize(union node *n)
7634{
7635 if (n == NULL)
7636 return;
7637 funcblocksize += nodesize[n->type];
7638 switch (n->type) {
7639 case NCMD:
7640 calcsize(n->ncmd.redirect);
7641 calcsize(n->ncmd.args);
7642 calcsize(n->ncmd.assign);
7643 break;
7644 case NPIPE:
7645 sizenodelist(n->npipe.cmdlist);
7646 break;
7647 case NREDIR:
7648 case NBACKGND:
7649 case NSUBSHELL:
7650 calcsize(n->nredir.redirect);
7651 calcsize(n->nredir.n);
7652 break;
7653 case NAND:
7654 case NOR:
7655 case NSEMI:
7656 case NWHILE:
7657 case NUNTIL:
7658 calcsize(n->nbinary.ch2);
7659 calcsize(n->nbinary.ch1);
7660 break;
7661 case NIF:
7662 calcsize(n->nif.elsepart);
7663 calcsize(n->nif.ifpart);
7664 calcsize(n->nif.test);
7665 break;
7666 case NFOR:
7667 funcstringsize += strlen(n->nfor.var) + 1;
7668 calcsize(n->nfor.body);
7669 calcsize(n->nfor.args);
7670 break;
7671 case NCASE:
7672 calcsize(n->ncase.cases);
7673 calcsize(n->ncase.expr);
7674 break;
7675 case NCLIST:
7676 calcsize(n->nclist.body);
7677 calcsize(n->nclist.pattern);
7678 calcsize(n->nclist.next);
7679 break;
7680 case NDEFUN:
7681 case NARG:
7682 sizenodelist(n->narg.backquote);
7683 funcstringsize += strlen(n->narg.text) + 1;
7684 calcsize(n->narg.next);
7685 break;
7686 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007687#if ENABLE_ASH_BASH_COMPAT
7688 case NTO2:
7689#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007690 case NCLOBBER:
7691 case NFROM:
7692 case NFROMTO:
7693 case NAPPEND:
7694 calcsize(n->nfile.fname);
7695 calcsize(n->nfile.next);
7696 break;
7697 case NTOFD:
7698 case NFROMFD:
7699 calcsize(n->ndup.vname);
7700 calcsize(n->ndup.next);
7701 break;
7702 case NHERE:
7703 case NXHERE:
7704 calcsize(n->nhere.doc);
7705 calcsize(n->nhere.next);
7706 break;
7707 case NNOT:
7708 calcsize(n->nnot.com);
7709 break;
7710 };
7711}
7712
7713static char *
7714nodeckstrdup(char *s)
7715{
7716 char *rtn = funcstring;
7717
7718 strcpy(funcstring, s);
7719 funcstring += strlen(s) + 1;
7720 return rtn;
7721}
7722
7723static union node *copynode(union node *);
7724
7725static struct nodelist *
7726copynodelist(struct nodelist *lp)
7727{
7728 struct nodelist *start;
7729 struct nodelist **lpp;
7730
7731 lpp = &start;
7732 while (lp) {
7733 *lpp = funcblock;
7734 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7735 (*lpp)->n = copynode(lp->n);
7736 lp = lp->next;
7737 lpp = &(*lpp)->next;
7738 }
7739 *lpp = NULL;
7740 return start;
7741}
7742
7743static union node *
7744copynode(union node *n)
7745{
7746 union node *new;
7747
7748 if (n == NULL)
7749 return NULL;
7750 new = funcblock;
7751 funcblock = (char *) funcblock + nodesize[n->type];
7752
7753 switch (n->type) {
7754 case NCMD:
7755 new->ncmd.redirect = copynode(n->ncmd.redirect);
7756 new->ncmd.args = copynode(n->ncmd.args);
7757 new->ncmd.assign = copynode(n->ncmd.assign);
7758 break;
7759 case NPIPE:
7760 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007761 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007762 break;
7763 case NREDIR:
7764 case NBACKGND:
7765 case NSUBSHELL:
7766 new->nredir.redirect = copynode(n->nredir.redirect);
7767 new->nredir.n = copynode(n->nredir.n);
7768 break;
7769 case NAND:
7770 case NOR:
7771 case NSEMI:
7772 case NWHILE:
7773 case NUNTIL:
7774 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7775 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7776 break;
7777 case NIF:
7778 new->nif.elsepart = copynode(n->nif.elsepart);
7779 new->nif.ifpart = copynode(n->nif.ifpart);
7780 new->nif.test = copynode(n->nif.test);
7781 break;
7782 case NFOR:
7783 new->nfor.var = nodeckstrdup(n->nfor.var);
7784 new->nfor.body = copynode(n->nfor.body);
7785 new->nfor.args = copynode(n->nfor.args);
7786 break;
7787 case NCASE:
7788 new->ncase.cases = copynode(n->ncase.cases);
7789 new->ncase.expr = copynode(n->ncase.expr);
7790 break;
7791 case NCLIST:
7792 new->nclist.body = copynode(n->nclist.body);
7793 new->nclist.pattern = copynode(n->nclist.pattern);
7794 new->nclist.next = copynode(n->nclist.next);
7795 break;
7796 case NDEFUN:
7797 case NARG:
7798 new->narg.backquote = copynodelist(n->narg.backquote);
7799 new->narg.text = nodeckstrdup(n->narg.text);
7800 new->narg.next = copynode(n->narg.next);
7801 break;
7802 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007803#if ENABLE_ASH_BASH_COMPAT
7804 case NTO2:
7805#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007806 case NCLOBBER:
7807 case NFROM:
7808 case NFROMTO:
7809 case NAPPEND:
7810 new->nfile.fname = copynode(n->nfile.fname);
7811 new->nfile.fd = n->nfile.fd;
7812 new->nfile.next = copynode(n->nfile.next);
7813 break;
7814 case NTOFD:
7815 case NFROMFD:
7816 new->ndup.vname = copynode(n->ndup.vname);
7817 new->ndup.dupfd = n->ndup.dupfd;
7818 new->ndup.fd = n->ndup.fd;
7819 new->ndup.next = copynode(n->ndup.next);
7820 break;
7821 case NHERE:
7822 case NXHERE:
7823 new->nhere.doc = copynode(n->nhere.doc);
7824 new->nhere.fd = n->nhere.fd;
7825 new->nhere.next = copynode(n->nhere.next);
7826 break;
7827 case NNOT:
7828 new->nnot.com = copynode(n->nnot.com);
7829 break;
7830 };
7831 new->type = n->type;
7832 return new;
7833}
7834
7835/*
7836 * Make a copy of a parse tree.
7837 */
7838static struct funcnode *
7839copyfunc(union node *n)
7840{
7841 struct funcnode *f;
7842 size_t blocksize;
7843
7844 funcblocksize = offsetof(struct funcnode, n);
7845 funcstringsize = 0;
7846 calcsize(n);
7847 blocksize = funcblocksize;
7848 f = ckmalloc(blocksize + funcstringsize);
7849 funcblock = (char *) f + offsetof(struct funcnode, n);
7850 funcstring = (char *) f + blocksize;
7851 copynode(n);
7852 f->count = 0;
7853 return f;
7854}
7855
7856/*
7857 * Define a shell function.
7858 */
7859static void
7860defun(char *name, union node *func)
7861{
7862 struct cmdentry entry;
7863
7864 INT_OFF;
7865 entry.cmdtype = CMDFUNCTION;
7866 entry.u.func = copyfunc(func);
7867 addcmdentry(name, &entry);
7868 INT_ON;
7869}
7870
7871static int evalskip; /* set if we are skipping commands */
7872/* reasons for skipping commands (see comment on breakcmd routine) */
7873#define SKIPBREAK (1 << 0)
7874#define SKIPCONT (1 << 1)
7875#define SKIPFUNC (1 << 2)
7876#define SKIPFILE (1 << 3)
7877#define SKIPEVAL (1 << 4)
7878static int skipcount; /* number of levels to skip */
7879static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007880static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007881
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007882/* forward decl way out to parsing code - dotrap needs it */
7883static int evalstring(char *s, int mask);
7884
7885/*
7886 * Called to execute a trap. Perhaps we should avoid entering new trap
7887 * handlers while we are executing a trap handler.
7888 */
7889static int
7890dotrap(void)
7891{
7892 char *p;
7893 char *q;
7894 int i;
7895 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007896 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007897
7898 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007899 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007900 xbarrier();
7901
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007902 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007903 if (!*q)
7904 continue;
7905 *q = '\0';
7906
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007907 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007908 if (!p)
7909 continue;
7910 skip = evalstring(p, SKIPEVAL);
7911 exitstatus = savestatus;
7912 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007913 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007914 }
7915
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007916 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007917}
7918
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007919/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007920static void evalloop(union node *, int);
7921static void evalfor(union node *, int);
7922static void evalcase(union node *, int);
7923static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007924static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007925static void evalpipe(union node *, int);
7926static void evalcommand(union node *, int);
7927static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007928static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007929
Eric Andersen62483552001-07-10 06:09:16 +00007930/*
Eric Andersenc470f442003-07-28 09:56:35 +00007931 * Evaluate a parse tree. The value is left in the global variable
7932 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007933 */
Eric Andersenc470f442003-07-28 09:56:35 +00007934static void
7935evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007936{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007937
7938 struct jmploc *volatile savehandler = exception_handler;
7939 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007940 int checkexit = 0;
7941 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007942 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007943
Eric Andersenc470f442003-07-28 09:56:35 +00007944 if (n == NULL) {
7945 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007946 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007947 }
Eric Andersenc470f442003-07-28 09:56:35 +00007948 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007949 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007950
7951 exception_handler = &jmploc;
7952 {
7953 int err = setjmp(jmploc.loc);
7954 if (err) {
7955 /* if it was a signal, check for trap handlers */
7956 if (exception == EXSIG)
7957 goto out;
7958 /* continue on the way out */
7959 exception_handler = savehandler;
7960 longjmp(exception_handler->loc, err);
7961 }
7962 }
7963
Eric Andersenc470f442003-07-28 09:56:35 +00007964 switch (n->type) {
7965 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007966#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007967 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007968 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007969 break;
7970#endif
7971 case NNOT:
7972 evaltree(n->nnot.com, EV_TESTED);
7973 status = !exitstatus;
7974 goto setstatus;
7975 case NREDIR:
7976 expredir(n->nredir.redirect);
7977 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7978 if (!status) {
7979 evaltree(n->nredir.n, flags & EV_TESTED);
7980 status = exitstatus;
7981 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007982 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00007983 goto setstatus;
7984 case NCMD:
7985 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007986 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007987 if (eflag && !(flags & EV_TESTED))
7988 checkexit = ~0;
7989 goto calleval;
7990 case NFOR:
7991 evalfn = evalfor;
7992 goto calleval;
7993 case NWHILE:
7994 case NUNTIL:
7995 evalfn = evalloop;
7996 goto calleval;
7997 case NSUBSHELL:
7998 case NBACKGND:
7999 evalfn = evalsubshell;
8000 goto calleval;
8001 case NPIPE:
8002 evalfn = evalpipe;
8003 goto checkexit;
8004 case NCASE:
8005 evalfn = evalcase;
8006 goto calleval;
8007 case NAND:
8008 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008009 case NSEMI: {
8010
Eric Andersenc470f442003-07-28 09:56:35 +00008011#if NAND + 1 != NOR
8012#error NAND + 1 != NOR
8013#endif
8014#if NOR + 1 != NSEMI
8015#error NOR + 1 != NSEMI
8016#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008017 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008018 evaltree(
8019 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008020 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008021 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008022 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008023 break;
8024 if (!evalskip) {
8025 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008026 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008027 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008028 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008029 evalfn(n, flags);
8030 break;
8031 }
8032 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008033 }
Eric Andersenc470f442003-07-28 09:56:35 +00008034 case NIF:
8035 evaltree(n->nif.test, EV_TESTED);
8036 if (evalskip)
8037 break;
8038 if (exitstatus == 0) {
8039 n = n->nif.ifpart;
8040 goto evaln;
8041 } else if (n->nif.elsepart) {
8042 n = n->nif.elsepart;
8043 goto evaln;
8044 }
8045 goto success;
8046 case NDEFUN:
8047 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008048 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008049 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008050 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008051 exitstatus = status;
8052 break;
8053 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008054
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008055 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008056 exception_handler = savehandler;
8057 out1:
8058 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008059 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008060 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008061 goto exexit;
8062
8063 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008064 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008065 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008066 }
Eric Andersen62483552001-07-10 06:09:16 +00008067}
8068
Eric Andersenc470f442003-07-28 09:56:35 +00008069#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8070static
8071#endif
8072void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8073
Eric Andersenc470f442003-07-28 09:56:35 +00008074static void
8075evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008076{
8077 int status;
8078
8079 loopnest++;
8080 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008081 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008082 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008083 int i;
8084
Eric Andersencb57d552001-06-28 07:25:16 +00008085 evaltree(n->nbinary.ch1, EV_TESTED);
8086 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008087 skipping:
8088 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008089 evalskip = 0;
8090 continue;
8091 }
8092 if (evalskip == SKIPBREAK && --skipcount <= 0)
8093 evalskip = 0;
8094 break;
8095 }
Eric Andersenc470f442003-07-28 09:56:35 +00008096 i = exitstatus;
8097 if (n->type != NWHILE)
8098 i = !i;
8099 if (i != 0)
8100 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008101 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008102 status = exitstatus;
8103 if (evalskip)
8104 goto skipping;
8105 }
8106 loopnest--;
8107 exitstatus = status;
8108}
8109
Eric Andersenc470f442003-07-28 09:56:35 +00008110static void
8111evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008112{
8113 struct arglist arglist;
8114 union node *argp;
8115 struct strlist *sp;
8116 struct stackmark smark;
8117
8118 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008119 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008120 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008121 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008122 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008123 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008124 if (evalskip)
8125 goto out;
8126 }
8127 *arglist.lastp = NULL;
8128
8129 exitstatus = 0;
8130 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008131 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008132 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008133 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008134 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008135 if (evalskip) {
8136 if (evalskip == SKIPCONT && --skipcount <= 0) {
8137 evalskip = 0;
8138 continue;
8139 }
8140 if (evalskip == SKIPBREAK && --skipcount <= 0)
8141 evalskip = 0;
8142 break;
8143 }
8144 }
8145 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008146 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008147 popstackmark(&smark);
8148}
8149
Eric Andersenc470f442003-07-28 09:56:35 +00008150static void
8151evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008152{
8153 union node *cp;
8154 union node *patp;
8155 struct arglist arglist;
8156 struct stackmark smark;
8157
8158 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008159 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008160 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008161 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008162 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008163 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8164 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008165 if (casematch(patp, arglist.list->text)) {
8166 if (evalskip == 0) {
8167 evaltree(cp->nclist.body, flags);
8168 }
8169 goto out;
8170 }
8171 }
8172 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008173 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008174 popstackmark(&smark);
8175}
8176
Eric Andersenc470f442003-07-28 09:56:35 +00008177/*
8178 * Kick off a subshell to evaluate a tree.
8179 */
Eric Andersenc470f442003-07-28 09:56:35 +00008180static void
8181evalsubshell(union node *n, int flags)
8182{
8183 struct job *jp;
8184 int backgnd = (n->type == NBACKGND);
8185 int status;
8186
8187 expredir(n->nredir.redirect);
8188 if (!backgnd && flags & EV_EXIT && !trap[0])
8189 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008190 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008191 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008192 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008193 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008194 flags |= EV_EXIT;
8195 if (backgnd)
8196 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008197 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008198 redirect(n->nredir.redirect, 0);
8199 evaltreenr(n->nredir.n, flags);
8200 /* never returns */
8201 }
8202 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008203 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008204 status = waitforjob(jp);
8205 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008206 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008207}
8208
Eric Andersenc470f442003-07-28 09:56:35 +00008209/*
8210 * Compute the names of the files in a redirection list.
8211 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008212static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008213static void
8214expredir(union node *n)
8215{
8216 union node *redir;
8217
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008218 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008219 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008220
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008221 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008222 fn.lastp = &fn.list;
8223 switch (redir->type) {
8224 case NFROMTO:
8225 case NFROM:
8226 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008227#if ENABLE_ASH_BASH_COMPAT
8228 case NTO2:
8229#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008230 case NCLOBBER:
8231 case NAPPEND:
8232 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008233#if ENABLE_ASH_BASH_COMPAT
8234 store_expfname:
8235#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008236 redir->nfile.expfname = fn.list->text;
8237 break;
8238 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008239 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008240 if (redir->ndup.vname) {
8241 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008242 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008243 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008244#if ENABLE_ASH_BASH_COMPAT
8245//FIXME: we used expandarg with different args!
8246 if (!isdigit_str9(fn.list->text)) {
8247 /* >&file, not >&fd */
8248 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8249 ash_msg_and_raise_error("redir error");
8250 redir->type = NTO2;
8251 goto store_expfname;
8252 }
8253#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008254 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008255 }
8256 break;
8257 }
8258 }
8259}
8260
Eric Andersencb57d552001-06-28 07:25:16 +00008261/*
Eric Andersencb57d552001-06-28 07:25:16 +00008262 * Evaluate a pipeline. All the processes in the pipeline are children
8263 * of the process creating the pipeline. (This differs from some versions
8264 * of the shell, which make the last process in a pipeline the parent
8265 * of all the rest.)
8266 */
Eric Andersenc470f442003-07-28 09:56:35 +00008267static void
8268evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008269{
8270 struct job *jp;
8271 struct nodelist *lp;
8272 int pipelen;
8273 int prevfd;
8274 int pip[2];
8275
Eric Andersenc470f442003-07-28 09:56:35 +00008276 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008277 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008278 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008279 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008280 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008281 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008282 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008283 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008284 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008285 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008286 pip[1] = -1;
8287 if (lp->next) {
8288 if (pipe(pip) < 0) {
8289 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008290 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008291 }
8292 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008293 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008294 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008295 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008296 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008297 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008298 if (prevfd > 0) {
8299 dup2(prevfd, 0);
8300 close(prevfd);
8301 }
8302 if (pip[1] > 1) {
8303 dup2(pip[1], 1);
8304 close(pip[1]);
8305 }
Eric Andersenc470f442003-07-28 09:56:35 +00008306 evaltreenr(lp->n, flags);
8307 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008308 }
8309 if (prevfd >= 0)
8310 close(prevfd);
8311 prevfd = pip[0];
8312 close(pip[1]);
8313 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008314 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008315 exitstatus = waitforjob(jp);
8316 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008317 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008318 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008319}
8320
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008321/*
8322 * Controls whether the shell is interactive or not.
8323 */
8324static void
8325setinteractive(int on)
8326{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008327 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008328
8329 if (++on == is_interactive)
8330 return;
8331 is_interactive = on;
8332 setsignal(SIGINT);
8333 setsignal(SIGQUIT);
8334 setsignal(SIGTERM);
8335#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8336 if (is_interactive > 1) {
8337 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008338 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008339
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008340 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008341 out1fmt(
8342 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008343 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008344 "Enter 'help' for a list of built-in commands."
8345 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008346 bb_banner);
8347 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008348 }
8349 }
8350#endif
8351}
8352
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008353static void
8354optschanged(void)
8355{
8356#if DEBUG
8357 opentrace();
8358#endif
8359 setinteractive(iflag);
8360 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008361#if ENABLE_FEATURE_EDITING_VI
8362 if (viflag)
8363 line_input_state->flags |= VI_MODE;
8364 else
8365 line_input_state->flags &= ~VI_MODE;
8366#else
8367 viflag = 0; /* forcibly keep the option off */
8368#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008369}
8370
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008371static struct localvar *localvars;
8372
8373/*
8374 * Called after a function returns.
8375 * Interrupts must be off.
8376 */
8377static void
8378poplocalvars(void)
8379{
8380 struct localvar *lvp;
8381 struct var *vp;
8382
8383 while ((lvp = localvars) != NULL) {
8384 localvars = lvp->next;
8385 vp = lvp->vp;
8386 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8387 if (vp == NULL) { /* $- saved */
8388 memcpy(optlist, lvp->text, sizeof(optlist));
8389 free((char*)lvp->text);
8390 optschanged();
8391 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8392 unsetvar(vp->text);
8393 } else {
8394 if (vp->func)
8395 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8396 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8397 free((char*)vp->text);
8398 vp->flags = lvp->flags;
8399 vp->text = lvp->text;
8400 }
8401 free(lvp);
8402 }
8403}
8404
8405static int
8406evalfun(struct funcnode *func, int argc, char **argv, int flags)
8407{
8408 volatile struct shparam saveparam;
8409 struct localvar *volatile savelocalvars;
8410 struct jmploc *volatile savehandler;
8411 struct jmploc jmploc;
8412 int e;
8413
8414 saveparam = shellparam;
8415 savelocalvars = localvars;
8416 e = setjmp(jmploc.loc);
8417 if (e) {
8418 goto funcdone;
8419 }
8420 INT_OFF;
8421 savehandler = exception_handler;
8422 exception_handler = &jmploc;
8423 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008424 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008425 func->count++;
8426 funcnest++;
8427 INT_ON;
8428 shellparam.nparam = argc - 1;
8429 shellparam.p = argv + 1;
8430#if ENABLE_ASH_GETOPTS
8431 shellparam.optind = 1;
8432 shellparam.optoff = -1;
8433#endif
8434 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008435 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008436 INT_OFF;
8437 funcnest--;
8438 freefunc(func);
8439 poplocalvars();
8440 localvars = savelocalvars;
8441 freeparam(&shellparam);
8442 shellparam = saveparam;
8443 exception_handler = savehandler;
8444 INT_ON;
8445 evalskip &= ~SKIPFUNC;
8446 return e;
8447}
8448
Denis Vlasenko131ae172007-02-18 13:00:19 +00008449#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008450static char **
8451parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008452{
8453 char *cp, c;
8454
8455 for (;;) {
8456 cp = *++argv;
8457 if (!cp)
8458 return 0;
8459 if (*cp++ != '-')
8460 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008461 c = *cp++;
8462 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008463 break;
8464 if (c == '-' && !*cp) {
8465 argv++;
8466 break;
8467 }
8468 do {
8469 switch (c) {
8470 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008471 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008472 break;
8473 default:
8474 /* run 'typecmd' for other options */
8475 return 0;
8476 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008477 c = *cp++;
8478 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008479 }
8480 return argv;
8481}
8482#endif
8483
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008484/*
8485 * Make a variable a local variable. When a variable is made local, it's
8486 * value and flags are saved in a localvar structure. The saved values
8487 * will be restored when the shell function returns. We handle the name
8488 * "-" as a special case.
8489 */
8490static void
8491mklocal(char *name)
8492{
8493 struct localvar *lvp;
8494 struct var **vpp;
8495 struct var *vp;
8496
8497 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008498 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008499 if (LONE_DASH(name)) {
8500 char *p;
8501 p = ckmalloc(sizeof(optlist));
8502 lvp->text = memcpy(p, optlist, sizeof(optlist));
8503 vp = NULL;
8504 } else {
8505 char *eq;
8506
8507 vpp = hashvar(name);
8508 vp = *findvar(vpp, name);
8509 eq = strchr(name, '=');
8510 if (vp == NULL) {
8511 if (eq)
8512 setvareq(name, VSTRFIXED);
8513 else
8514 setvar(name, NULL, VSTRFIXED);
8515 vp = *vpp; /* the new variable */
8516 lvp->flags = VUNSET;
8517 } else {
8518 lvp->text = vp->text;
8519 lvp->flags = vp->flags;
8520 vp->flags |= VSTRFIXED|VTEXTFIXED;
8521 if (eq)
8522 setvareq(name, 0);
8523 }
8524 }
8525 lvp->vp = vp;
8526 lvp->next = localvars;
8527 localvars = lvp;
8528 INT_ON;
8529}
8530
8531/*
8532 * The "local" command.
8533 */
8534static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008535localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008536{
8537 char *name;
8538
8539 argv = argptr;
8540 while ((name = *argv++) != NULL) {
8541 mklocal(name);
8542 }
8543 return 0;
8544}
8545
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008546static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008547falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008548{
8549 return 1;
8550}
8551
8552static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008553truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008554{
8555 return 0;
8556}
8557
8558static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008559execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008560{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008561 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008562 iflag = 0; /* exit on error */
8563 mflag = 0;
8564 optschanged();
8565 shellexec(argv + 1, pathval(), 0);
8566 }
8567 return 0;
8568}
8569
8570/*
8571 * The return command.
8572 */
8573static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008574returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008575{
8576 /*
8577 * If called outside a function, do what ksh does;
8578 * skip the rest of the file.
8579 */
8580 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8581 return argv[1] ? number(argv[1]) : exitstatus;
8582}
8583
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008584/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008585static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008586static int dotcmd(int, char **);
8587static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008588static int exitcmd(int, char **);
8589static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008590#if ENABLE_ASH_GETOPTS
8591static int getoptscmd(int, char **);
8592#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008593#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008594static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008595#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008596#if ENABLE_ASH_MATH_SUPPORT
8597static int letcmd(int, char **);
8598#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008599static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008600static int setcmd(int, char **);
8601static int shiftcmd(int, char **);
8602static int timescmd(int, char **);
8603static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008604static int umaskcmd(int, char **);
8605static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008606static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008607
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008608#define BUILTIN_NOSPEC "0"
8609#define BUILTIN_SPECIAL "1"
8610#define BUILTIN_REGULAR "2"
8611#define BUILTIN_SPEC_REG "3"
8612#define BUILTIN_ASSIGN "4"
8613#define BUILTIN_SPEC_ASSG "5"
8614#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008615#define BUILTIN_SPEC_REG_ASSG "7"
8616
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008617/* We do not handle [[ expr ]] bashism bash-compatibly,
8618 * we make it a synonym of [ expr ].
8619 * Basically, word splitting and pathname expansion should NOT be performed
8620 * Examples:
8621 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8622 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8623 * Additional operators:
8624 * || and && should work as -o and -a
8625 * =~ regexp match
8626 * Apart from the above, [[ expr ]] should work as [ expr ]
8627 */
8628
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008629#define echocmd echo_main
8630#define printfcmd printf_main
8631#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008632
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008633/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008634static const struct builtincmd builtintab[] = {
8635 { BUILTIN_SPEC_REG ".", dotcmd },
8636 { BUILTIN_SPEC_REG ":", truecmd },
8637#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008638 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008639#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008640 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008641#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008642#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008643#if ENABLE_ASH_ALIAS
8644 { BUILTIN_REG_ASSG "alias", aliascmd },
8645#endif
8646#if JOBS
8647 { BUILTIN_REGULAR "bg", fg_bgcmd },
8648#endif
8649 { BUILTIN_SPEC_REG "break", breakcmd },
8650 { BUILTIN_REGULAR "cd", cdcmd },
8651 { BUILTIN_NOSPEC "chdir", cdcmd },
8652#if ENABLE_ASH_CMDCMD
8653 { BUILTIN_REGULAR "command", commandcmd },
8654#endif
8655 { BUILTIN_SPEC_REG "continue", breakcmd },
8656#if ENABLE_ASH_BUILTIN_ECHO
8657 { BUILTIN_REGULAR "echo", echocmd },
8658#endif
8659 { BUILTIN_SPEC_REG "eval", evalcmd },
8660 { BUILTIN_SPEC_REG "exec", execcmd },
8661 { BUILTIN_SPEC_REG "exit", exitcmd },
8662 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8663 { BUILTIN_REGULAR "false", falsecmd },
8664#if JOBS
8665 { BUILTIN_REGULAR "fg", fg_bgcmd },
8666#endif
8667#if ENABLE_ASH_GETOPTS
8668 { BUILTIN_REGULAR "getopts", getoptscmd },
8669#endif
8670 { BUILTIN_NOSPEC "hash", hashcmd },
8671#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8672 { BUILTIN_NOSPEC "help", helpcmd },
8673#endif
8674#if JOBS
8675 { BUILTIN_REGULAR "jobs", jobscmd },
8676 { BUILTIN_REGULAR "kill", killcmd },
8677#endif
8678#if ENABLE_ASH_MATH_SUPPORT
8679 { BUILTIN_NOSPEC "let", letcmd },
8680#endif
8681 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008682#if ENABLE_ASH_BUILTIN_PRINTF
8683 { BUILTIN_REGULAR "printf", printfcmd },
8684#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008685 { BUILTIN_NOSPEC "pwd", pwdcmd },
8686 { BUILTIN_REGULAR "read", readcmd },
8687 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8688 { BUILTIN_SPEC_REG "return", returncmd },
8689 { BUILTIN_SPEC_REG "set", setcmd },
8690 { BUILTIN_SPEC_REG "shift", shiftcmd },
8691 { BUILTIN_SPEC_REG "source", dotcmd },
8692#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008693 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008694#endif
8695 { BUILTIN_SPEC_REG "times", timescmd },
8696 { BUILTIN_SPEC_REG "trap", trapcmd },
8697 { BUILTIN_REGULAR "true", truecmd },
8698 { BUILTIN_NOSPEC "type", typecmd },
8699 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8700 { BUILTIN_REGULAR "umask", umaskcmd },
8701#if ENABLE_ASH_ALIAS
8702 { BUILTIN_REGULAR "unalias", unaliascmd },
8703#endif
8704 { BUILTIN_SPEC_REG "unset", unsetcmd },
8705 { BUILTIN_REGULAR "wait", waitcmd },
8706};
8707
Denis Vlasenko80591b02008-03-25 07:49:43 +00008708/* Should match the above table! */
8709#define COMMANDCMD (builtintab + \
8710 2 + \
8711 1 * ENABLE_ASH_BUILTIN_TEST + \
8712 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8713 1 * ENABLE_ASH_ALIAS + \
8714 1 * ENABLE_ASH_JOB_CONTROL + \
8715 3)
8716#define EXECCMD (builtintab + \
8717 2 + \
8718 1 * ENABLE_ASH_BUILTIN_TEST + \
8719 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8720 1 * ENABLE_ASH_ALIAS + \
8721 1 * ENABLE_ASH_JOB_CONTROL + \
8722 3 + \
8723 1 * ENABLE_ASH_CMDCMD + \
8724 1 + \
8725 ENABLE_ASH_BUILTIN_ECHO + \
8726 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008727
8728/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008729 * Search the table of builtin commands.
8730 */
8731static struct builtincmd *
8732find_builtin(const char *name)
8733{
8734 struct builtincmd *bp;
8735
8736 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008737 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008738 pstrcmp
8739 );
8740 return bp;
8741}
8742
8743/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008744 * Execute a simple command.
8745 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008746static int
8747isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008748{
8749 const char *q = endofname(p);
8750 if (p == q)
8751 return 0;
8752 return *q == '=';
8753}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008754static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008755bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008756{
8757 /* Preserve exitstatus of a previous possible redirection
8758 * as POSIX mandates */
8759 return back_exitstatus;
8760}
Eric Andersenc470f442003-07-28 09:56:35 +00008761static void
8762evalcommand(union node *cmd, int flags)
8763{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008764 static const struct builtincmd null_bltin = {
8765 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008766 };
Eric Andersenc470f442003-07-28 09:56:35 +00008767 struct stackmark smark;
8768 union node *argp;
8769 struct arglist arglist;
8770 struct arglist varlist;
8771 char **argv;
8772 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008773 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008774 struct cmdentry cmdentry;
8775 struct job *jp;
8776 char *lastarg;
8777 const char *path;
8778 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008779 int status;
8780 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008781 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008782 smallint cmd_is_exec;
8783 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008784
8785 /* First expand the arguments. */
8786 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8787 setstackmark(&smark);
8788 back_exitstatus = 0;
8789
8790 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008791 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008792 varlist.lastp = &varlist.list;
8793 *varlist.lastp = NULL;
8794 arglist.lastp = &arglist.list;
8795 *arglist.lastp = NULL;
8796
8797 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008798 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008799 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8800 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8801 }
8802
Eric Andersenc470f442003-07-28 09:56:35 +00008803 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8804 struct strlist **spp;
8805
8806 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008807 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008808 expandarg(argp, &arglist, EXP_VARTILDE);
8809 else
8810 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8811
Eric Andersenc470f442003-07-28 09:56:35 +00008812 for (sp = *spp; sp; sp = sp->next)
8813 argc++;
8814 }
8815
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008816 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008817 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008818 TRACE(("evalcommand arg: %s\n", sp->text));
8819 *nargv++ = sp->text;
8820 }
8821 *nargv = NULL;
8822
8823 lastarg = NULL;
8824 if (iflag && funcnest == 0 && argc > 0)
8825 lastarg = nargv[-1];
8826
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008827 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008828 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008829 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008830
8831 path = vpath.text;
8832 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8833 struct strlist **spp;
8834 char *p;
8835
8836 spp = varlist.lastp;
8837 expandarg(argp, &varlist, EXP_VARTILDE);
8838
8839 /*
8840 * Modify the command lookup path, if a PATH= assignment
8841 * is present
8842 */
8843 p = (*spp)->text;
8844 if (varequal(p, path))
8845 path = p;
8846 }
8847
8848 /* Print the command if xflag is set. */
8849 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008850 int n;
8851 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008852
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008853 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008854 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008855
8856 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008857 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008858 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008859 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008860 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008861 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008862 p--;
8863 }
8864 }
8865 sp = arglist.list;
8866 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008867 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008868 }
8869
8870 cmd_is_exec = 0;
8871 spclbltin = -1;
8872
8873 /* Now locate the command. */
8874 if (argc) {
8875 const char *oldpath;
8876 int cmd_flag = DO_ERR;
8877
8878 path += 5;
8879 oldpath = path;
8880 for (;;) {
8881 find_command(argv[0], &cmdentry, cmd_flag, path);
8882 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008883 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008884 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008885 goto bail;
8886 }
8887
8888 /* implement bltin and command here */
8889 if (cmdentry.cmdtype != CMDBUILTIN)
8890 break;
8891 if (spclbltin < 0)
8892 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8893 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008894 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008895#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008896 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008897 path = oldpath;
8898 nargv = parse_command_args(argv, &path);
8899 if (!nargv)
8900 break;
8901 argc -= nargv - argv;
8902 argv = nargv;
8903 cmd_flag |= DO_NOFUNC;
8904 } else
8905#endif
8906 break;
8907 }
8908 }
8909
8910 if (status) {
8911 /* We have a redirection error. */
8912 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008913 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008914 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008915 exitstatus = status;
8916 goto out;
8917 }
8918
8919 /* Execute the command. */
8920 switch (cmdentry.cmdtype) {
8921 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008922#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008923 {
8924 /* find_command() encodes applet_no as (-2 - applet_no) */
8925 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008926 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008927 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008928 /* run <applet>_main() */
8929 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008930 break;
8931 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008932 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008933#endif
8934
Eric Andersenc470f442003-07-28 09:56:35 +00008935 /* Fork off a child process if necessary. */
8936 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008937 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008938 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008939 if (forkshell(jp, cmd, FORK_FG) != 0) {
8940 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008941 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008942 break;
8943 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008944 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008945 }
8946 listsetvar(varlist.list, VEXPORT|VSTACK);
8947 shellexec(argv, path, cmdentry.u.index);
8948 /* NOTREACHED */
8949
8950 case CMDBUILTIN:
8951 cmdenviron = varlist.list;
8952 if (cmdenviron) {
8953 struct strlist *list = cmdenviron;
8954 int i = VNOSET;
8955 if (spclbltin > 0 || argc == 0) {
8956 i = 0;
8957 if (cmd_is_exec && argc > 1)
8958 i = VEXPORT;
8959 }
8960 listsetvar(list, i);
8961 }
8962 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8963 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008964 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008965 if (i == EXEXIT)
8966 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008967 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008968 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008969 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008970 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008971 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008972 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008973 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008974 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008975 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008976 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008977 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008978 }
8979 break;
8980
8981 case CMDFUNCTION:
8982 listsetvar(varlist.list, 0);
8983 if (evalfun(cmdentry.u.func, argc, argv, flags))
8984 goto raise;
8985 break;
8986 }
8987
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008988 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008989 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008990 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008991 /* dsl: I think this is intended to be used to support
8992 * '_' in 'vi' command mode during line editing...
8993 * However I implemented that within libedit itself.
8994 */
8995 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008996 }
Eric Andersenc470f442003-07-28 09:56:35 +00008997 popstackmark(&smark);
8998}
8999
9000static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009001evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9002{
Eric Andersenc470f442003-07-28 09:56:35 +00009003 char *volatile savecmdname;
9004 struct jmploc *volatile savehandler;
9005 struct jmploc jmploc;
9006 int i;
9007
9008 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009009 i = setjmp(jmploc.loc);
9010 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009011 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009012 savehandler = exception_handler;
9013 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009014 commandname = argv[0];
9015 argptr = argv + 1;
9016 optptr = NULL; /* initialize nextopt */
9017 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009018 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009019 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009020 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009021 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009022 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009023// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009024 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009025
9026 return i;
9027}
9028
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009029static int
9030goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009031{
9032 return !*endofname(p);
9033}
9034
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009035
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009036/*
9037 * Search for a command. This is called before we fork so that the
9038 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009039 * the child. The check for "goodname" is an overly conservative
9040 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009041 */
Eric Andersenc470f442003-07-28 09:56:35 +00009042static void
9043prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009044{
9045 struct cmdentry entry;
9046
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009047 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9048 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009049}
9050
Eric Andersencb57d552001-06-28 07:25:16 +00009051
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009052/* ============ Builtin commands
9053 *
9054 * Builtin commands whose functions are closely tied to evaluation
9055 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009056 */
9057
9058/*
Eric Andersencb57d552001-06-28 07:25:16 +00009059 * Handle break and continue commands. Break, continue, and return are
9060 * all handled by setting the evalskip flag. The evaluation routines
9061 * above all check this flag, and if it is set they start skipping
9062 * commands rather than executing them. The variable skipcount is
9063 * the number of loops to break/continue, or the number of function
9064 * levels to return. (The latter is always 1.) It should probably
9065 * be an error to break out of more loops than exist, but it isn't
9066 * in the standard shell so we don't make it one here.
9067 */
Eric Andersenc470f442003-07-28 09:56:35 +00009068static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009069breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009070{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009071 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009072
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009073 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009074 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009075 if (n > loopnest)
9076 n = loopnest;
9077 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009078 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009079 skipcount = n;
9080 }
9081 return 0;
9082}
9083
Eric Andersenc470f442003-07-28 09:56:35 +00009084
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009085/* ============ input.c
9086 *
Eric Andersen90898442003-08-06 11:20:52 +00009087 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009088 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009089
Eric Andersenc470f442003-07-28 09:56:35 +00009090#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009091
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009092enum {
9093 INPUT_PUSH_FILE = 1,
9094 INPUT_NOFILE_OK = 2,
9095};
Eric Andersencb57d552001-06-28 07:25:16 +00009096
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009097static int plinno = 1; /* input line number */
9098/* number of characters left in input buffer */
9099static int parsenleft; /* copy of parsefile->nleft */
9100static int parselleft; /* copy of parsefile->lleft */
9101/* next character in input buffer */
9102static char *parsenextc; /* copy of parsefile->nextc */
9103
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009104static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009105/* values of checkkwd variable */
9106#define CHKALIAS 0x1
9107#define CHKKWD 0x2
9108#define CHKNL 0x4
9109
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009110static void
9111popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009112{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009113 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009114
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009115 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009116#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009117 if (sp->ap) {
9118 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9119 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009120 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009121 if (sp->string != sp->ap->val) {
9122 free(sp->string);
9123 }
9124 sp->ap->flag &= ~ALIASINUSE;
9125 if (sp->ap->flag & ALIASDEAD) {
9126 unalias(sp->ap->name);
9127 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009128 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009129#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009130 parsenextc = sp->prevstring;
9131 parsenleft = sp->prevnleft;
9132/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009133 g_parsefile->strpush = sp->prev;
9134 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009135 free(sp);
9136 INT_ON;
9137}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009138
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009139static int
9140preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009141{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009142 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009143 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009144 parsenextc = buf;
9145
Denis Vlasenko38f63192007-01-22 09:03:07 +00009146#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009147 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009148 if (!iflag || g_parsefile->fd)
9149 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009150 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009151#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009152 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009153#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009154 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9155 if (nr == 0) {
9156 /* Ctrl+C pressed */
9157 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009158 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009159 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009160 raise(SIGINT);
9161 return 1;
9162 }
Eric Andersenc470f442003-07-28 09:56:35 +00009163 goto retry;
9164 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009165 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009166 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009167 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009168 }
Eric Andersencb57d552001-06-28 07:25:16 +00009169 }
9170#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009171 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009172#endif
9173
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009174#if 0
9175/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009176 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009177 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009178 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009179 if (flags >= 0 && (flags & O_NONBLOCK)) {
9180 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009181 if (fcntl(0, F_SETFL, flags) >= 0) {
9182 out2str("sh: turning off NDELAY mode\n");
9183 goto retry;
9184 }
9185 }
9186 }
9187 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009188#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009189 return nr;
9190}
9191
9192/*
9193 * Refill the input buffer and return the next input character:
9194 *
9195 * 1) If a string was pushed back on the input, pop it;
9196 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9197 * from a string so we can't refill the buffer, return EOF.
9198 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9199 * 4) Process input up to the next newline, deleting nul characters.
9200 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009201static int
Eric Andersenc470f442003-07-28 09:56:35 +00009202preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009203{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009204 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009205 int more;
9206 char savec;
9207
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009208 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009209#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009210 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009211 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009212 return PEOA;
9213 }
Eric Andersen2870d962001-07-02 17:27:21 +00009214#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009215 popstring();
9216 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009217 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009218 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009219 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009220 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009221 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009222
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009223 more = parselleft;
9224 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009225 again:
9226 more = preadfd();
9227 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009228 parselleft = parsenleft = EOF_NLEFT;
9229 return PEOF;
9230 }
9231 }
9232
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009233 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009234
9235 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009236 for (;;) {
9237 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009238
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009239 more--;
9240 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009241
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009242 if (!c)
9243 memmove(q, q + 1, more);
9244 else {
9245 q++;
9246 if (c == '\n') {
9247 parsenleft = q - parsenextc - 1;
9248 break;
9249 }
Eric Andersencb57d552001-06-28 07:25:16 +00009250 }
9251
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009252 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009253 parsenleft = q - parsenextc - 1;
9254 if (parsenleft < 0)
9255 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009256 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009257 }
9258 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009259 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009260
9261 savec = *q;
9262 *q = '\0';
9263
9264 if (vflag) {
9265 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009266 }
9267
9268 *q = savec;
9269
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009270 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009271}
9272
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009273#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009274static int
9275pgetc(void)
9276{
9277 return pgetc_as_macro();
9278}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009279
9280#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9281#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009282#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009283#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009284#endif
9285
9286/*
9287 * Same as pgetc(), but ignores PEOA.
9288 */
9289#if ENABLE_ASH_ALIAS
9290static int
9291pgetc2(void)
9292{
9293 int c;
9294
9295 do {
9296 c = pgetc_macro();
9297 } while (c == PEOA);
9298 return c;
9299}
9300#else
9301static int
9302pgetc2(void)
9303{
9304 return pgetc_macro();
9305}
9306#endif
9307
9308/*
9309 * Read a line from the script.
9310 */
9311static char *
9312pfgets(char *line, int len)
9313{
9314 char *p = line;
9315 int nleft = len;
9316 int c;
9317
9318 while (--nleft > 0) {
9319 c = pgetc2();
9320 if (c == PEOF) {
9321 if (p == line)
9322 return NULL;
9323 break;
9324 }
9325 *p++ = c;
9326 if (c == '\n')
9327 break;
9328 }
9329 *p = '\0';
9330 return line;
9331}
9332
Eric Andersenc470f442003-07-28 09:56:35 +00009333/*
9334 * Undo the last call to pgetc. Only one character may be pushed back.
9335 * PEOF may be pushed back.
9336 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009337static void
Eric Andersenc470f442003-07-28 09:56:35 +00009338pungetc(void)
9339{
9340 parsenleft++;
9341 parsenextc--;
9342}
Eric Andersencb57d552001-06-28 07:25:16 +00009343
9344/*
9345 * Push a string back onto the input at this current parsefile level.
9346 * We handle aliases this way.
9347 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009348#if !ENABLE_ASH_ALIAS
9349#define pushstring(s, ap) pushstring(s)
9350#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009351static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009352pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009353{
Eric Andersencb57d552001-06-28 07:25:16 +00009354 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009355 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009356
Eric Andersenc470f442003-07-28 09:56:35 +00009357 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009358 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009359/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009360 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009361 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009362 sp->prev = g_parsefile->strpush;
9363 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009364 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009365 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009366 sp->prevstring = parsenextc;
9367 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009368#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009369 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009370 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009371 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009372 sp->string = s;
9373 }
Eric Andersen2870d962001-07-02 17:27:21 +00009374#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009375 parsenextc = s;
9376 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009377 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009378}
9379
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009380/*
9381 * To handle the "." command, a stack of input files is used. Pushfile
9382 * adds a new entry to the stack and popfile restores the previous level.
9383 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009384static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009385pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009386{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009387 struct parsefile *pf;
9388
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009389 g_parsefile->nleft = parsenleft;
9390 g_parsefile->lleft = parselleft;
9391 g_parsefile->nextc = parsenextc;
9392 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009393 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009394 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009395 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009396 /*pf->strpush = NULL; - ckzalloc did it */
9397 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009398 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009399}
9400
9401static void
9402popfile(void)
9403{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009404 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009405
Denis Vlasenkob012b102007-02-19 22:43:01 +00009406 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009407 if (pf->fd >= 0)
9408 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009409 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009410 while (pf->strpush)
9411 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009412 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009413 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009414 parsenleft = g_parsefile->nleft;
9415 parselleft = g_parsefile->lleft;
9416 parsenextc = g_parsefile->nextc;
9417 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009418 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009419}
9420
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009421/*
9422 * Return to top level.
9423 */
9424static void
9425popallfiles(void)
9426{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009427 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009428 popfile();
9429}
9430
9431/*
9432 * Close the file(s) that the shell is reading commands from. Called
9433 * after a fork is done.
9434 */
9435static void
9436closescript(void)
9437{
9438 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009439 if (g_parsefile->fd > 0) {
9440 close(g_parsefile->fd);
9441 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009442 }
9443}
9444
9445/*
9446 * Like setinputfile, but takes an open file descriptor. Call this with
9447 * interrupts off.
9448 */
9449static void
9450setinputfd(int fd, int push)
9451{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009452 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009453 if (push) {
9454 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009455 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009456 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009457 g_parsefile->fd = fd;
9458 if (g_parsefile->buf == NULL)
9459 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009460 parselleft = parsenleft = 0;
9461 plinno = 1;
9462}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009463
Eric Andersenc470f442003-07-28 09:56:35 +00009464/*
9465 * Set the input to take input from a file. If push is set, push the
9466 * old input onto the stack first.
9467 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009468static int
9469setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009470{
9471 int fd;
9472 int fd2;
9473
Denis Vlasenkob012b102007-02-19 22:43:01 +00009474 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009475 fd = open(fname, O_RDONLY);
9476 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009477 if (flags & INPUT_NOFILE_OK)
9478 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009479 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009480 }
Eric Andersenc470f442003-07-28 09:56:35 +00009481 if (fd < 10) {
9482 fd2 = copyfd(fd, 10);
9483 close(fd);
9484 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009485 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009486 fd = fd2;
9487 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009488 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009489 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009490 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009491 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009492}
9493
Eric Andersencb57d552001-06-28 07:25:16 +00009494/*
9495 * Like setinputfile, but takes input from a string.
9496 */
Eric Andersenc470f442003-07-28 09:56:35 +00009497static void
9498setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009499{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009500 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009501 pushfile();
9502 parsenextc = string;
9503 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009504 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009505 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009506 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009507}
9508
9509
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009510/* ============ mail.c
9511 *
9512 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009513 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009514
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009515#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009516
Eric Andersencb57d552001-06-28 07:25:16 +00009517#define MAXMBOXES 10
9518
Eric Andersenc470f442003-07-28 09:56:35 +00009519/* times of mailboxes */
9520static time_t mailtime[MAXMBOXES];
9521/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009522static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009523
Eric Andersencb57d552001-06-28 07:25:16 +00009524/*
Eric Andersenc470f442003-07-28 09:56:35 +00009525 * Print appropriate message(s) if mail has arrived.
9526 * If mail_var_path_changed is set,
9527 * then the value of MAIL has mail_var_path_changed,
9528 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009529 */
Eric Andersenc470f442003-07-28 09:56:35 +00009530static void
9531chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009532{
Eric Andersencb57d552001-06-28 07:25:16 +00009533 const char *mpath;
9534 char *p;
9535 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009536 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009537 struct stackmark smark;
9538 struct stat statb;
9539
Eric Andersencb57d552001-06-28 07:25:16 +00009540 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009541 mpath = mpathset() ? mpathval() : mailval();
9542 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009543 p = padvance(&mpath, nullstr);
9544 if (p == NULL)
9545 break;
9546 if (*p == '\0')
9547 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009548 for (q = p; *q; q++)
9549 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009550#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009551 if (q[-1] != '/')
9552 abort();
9553#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009554 q[-1] = '\0'; /* delete trailing '/' */
9555 if (stat(p, &statb) < 0) {
9556 *mtp = 0;
9557 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009558 }
Eric Andersenc470f442003-07-28 09:56:35 +00009559 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9560 fprintf(
9561 stderr, snlfmt,
9562 pathopt ? pathopt : "you have mail"
9563 );
9564 }
9565 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009566 }
Eric Andersenc470f442003-07-28 09:56:35 +00009567 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009568 popstackmark(&smark);
9569}
Eric Andersencb57d552001-06-28 07:25:16 +00009570
Eric Andersenc470f442003-07-28 09:56:35 +00009571static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009572changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009573{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009574 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009575}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009576
Denis Vlasenko131ae172007-02-18 13:00:19 +00009577#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009578
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009579
9580/* ============ ??? */
9581
Eric Andersencb57d552001-06-28 07:25:16 +00009582/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009583 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009584 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009585static void
9586setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009587{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009588 char **newparam;
9589 char **ap;
9590 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009591
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009592 for (nparam = 0; argv[nparam]; nparam++)
9593 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009594 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9595 while (*argv) {
9596 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009597 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009598 *ap = NULL;
9599 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009600 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009601 shellparam.nparam = nparam;
9602 shellparam.p = newparam;
9603#if ENABLE_ASH_GETOPTS
9604 shellparam.optind = 1;
9605 shellparam.optoff = -1;
9606#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009607}
9608
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009609/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009610 * Process shell options. The global variable argptr contains a pointer
9611 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009612 *
9613 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9614 * For a non-interactive shell, an error condition encountered
9615 * by a special built-in ... shall cause the shell to write a diagnostic message
9616 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009617 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009618 * ...
9619 * Utility syntax error (option or operand error) Shall exit
9620 * ...
9621 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9622 * we see that bash does not do that (set "finishes" with error code 1 instead,
9623 * and shell continues), and people rely on this behavior!
9624 * Testcase:
9625 * set -o barfoo 2>/dev/null
9626 * echo $?
9627 *
9628 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009629 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009630static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009631plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009632{
9633 int i;
9634
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009635 if (name) {
9636 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009637 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009638 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009639 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009640 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009641 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009642 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009643 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009644 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009645 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009646 if (val) {
9647 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9648 } else {
9649 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9650 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009651 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009652 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009653}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009654static void
9655setoption(int flag, int val)
9656{
9657 int i;
9658
9659 for (i = 0; i < NOPTS; i++) {
9660 if (optletters(i) == flag) {
9661 optlist[i] = val;
9662 return;
9663 }
9664 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009665 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009666 /* NOTREACHED */
9667}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009668static int
Eric Andersenc470f442003-07-28 09:56:35 +00009669options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009670{
9671 char *p;
9672 int val;
9673 int c;
9674
9675 if (cmdline)
9676 minusc = NULL;
9677 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009678 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009679 if (c != '-' && c != '+')
9680 break;
9681 argptr++;
9682 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009683 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009684 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009685 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009686 if (!cmdline) {
9687 /* "-" means turn off -x and -v */
9688 if (p[0] == '\0')
9689 xflag = vflag = 0;
9690 /* "--" means reset params */
9691 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009692 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009693 }
Eric Andersenc470f442003-07-28 09:56:35 +00009694 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009695 }
Eric Andersencb57d552001-06-28 07:25:16 +00009696 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009697 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009698 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009699 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009700 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009701 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009702 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009703 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009704 /* it already printed err message */
9705 return 1; /* error */
9706 }
Eric Andersencb57d552001-06-28 07:25:16 +00009707 if (*argptr)
9708 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009709 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9710 isloginsh = 1;
9711 /* bash does not accept +-login, we also won't */
9712 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009713 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009714 isloginsh = 1;
9715 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009716 } else {
9717 setoption(c, val);
9718 }
9719 }
9720 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009721 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009722}
9723
Eric Andersencb57d552001-06-28 07:25:16 +00009724/*
Eric Andersencb57d552001-06-28 07:25:16 +00009725 * The shift builtin command.
9726 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009727static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009728shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009729{
9730 int n;
9731 char **ap1, **ap2;
9732
9733 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009734 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009735 n = number(argv[1]);
9736 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009737 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009738 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009739 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009740 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009741 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009742 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009743 }
9744 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009745 while ((*ap2++ = *ap1++) != NULL)
9746 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009747#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009748 shellparam.optind = 1;
9749 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009750#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009751 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009752 return 0;
9753}
9754
Eric Andersencb57d552001-06-28 07:25:16 +00009755/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009756 * POSIX requires that 'set' (but not export or readonly) output the
9757 * variables in lexicographic order - by the locale's collating order (sigh).
9758 * Maybe we could keep them in an ordered balanced binary tree
9759 * instead of hashed lists.
9760 * For now just roll 'em through qsort for printing...
9761 */
9762static int
9763showvars(const char *sep_prefix, int on, int off)
9764{
9765 const char *sep;
9766 char **ep, **epend;
9767
9768 ep = listvars(on, off, &epend);
9769 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9770
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009771 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009772
9773 for (; ep < epend; ep++) {
9774 const char *p;
9775 const char *q;
9776
9777 p = strchrnul(*ep, '=');
9778 q = nullstr;
9779 if (*p)
9780 q = single_quote(++p);
9781 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9782 }
9783 return 0;
9784}
9785
9786/*
Eric Andersencb57d552001-06-28 07:25:16 +00009787 * The set command builtin.
9788 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009789static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009790setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009791{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009792 int retval;
9793
Denis Vlasenko68404f12008-03-17 09:00:54 +00009794 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009795 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009796 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009797 retval = 1;
9798 if (!options(0)) { /* if no parse error... */
9799 retval = 0;
9800 optschanged();
9801 if (*argptr != NULL) {
9802 setparam(argptr);
9803 }
Eric Andersencb57d552001-06-28 07:25:16 +00009804 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009805 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009806 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009807}
9808
Denis Vlasenko131ae172007-02-18 13:00:19 +00009809#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009810static void
9811change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009812{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009813 /* Galois LFSR parameter */
9814 /* Taps at 32 31 29 1: */
9815 enum { MASK = 0x8000000b };
9816 /* Another example - taps at 32 31 30 10: */
9817 /* MASK = 0x00400007 */
9818
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009819 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009820 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009821 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009822
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009823 /* LCG has period of 2^32 and alternating lowest bit */
9824 random_LCG = 1664525 * random_LCG + 1013904223;
9825 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9826 t = (random_galois_LFSR << 1);
9827 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9828 t ^= MASK;
9829 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009830 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009831 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009832 * for $RANDOM range. Combining with subtraction is
9833 * just for fun. + and ^ would work equally well. */
9834 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009835 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009836 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009837 vrandom.flags &= ~VNOFUNC;
9838 } else {
9839 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009840 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009841 }
Eric Andersenef02f822004-03-11 13:34:24 +00009842}
Eric Andersen16767e22004-03-16 05:14:10 +00009843#endif
9844
Denis Vlasenko131ae172007-02-18 13:00:19 +00009845#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009846static int
Eric Andersenc470f442003-07-28 09:56:35 +00009847getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009848{
9849 char *p, *q;
9850 char c = '?';
9851 int done = 0;
9852 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009853 char s[12];
9854 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009855
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009856 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009857 return 1;
9858 optnext = optfirst + *param_optind - 1;
9859
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009860 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009861 p = NULL;
9862 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009863 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009864 if (p == NULL || *p == '\0') {
9865 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009866 p = *optnext;
9867 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009868 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009869 p = NULL;
9870 done = 1;
9871 goto out;
9872 }
9873 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009874 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009875 goto atend;
9876 }
9877
9878 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009879 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009880 if (*q == '\0') {
9881 if (optstr[0] == ':') {
9882 s[0] = c;
9883 s[1] = '\0';
9884 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009885 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009886 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009887 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009888 }
9889 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009890 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009891 }
9892 if (*++q == ':')
9893 q++;
9894 }
9895
9896 if (*++q == ':') {
9897 if (*p == '\0' && (p = *optnext) == NULL) {
9898 if (optstr[0] == ':') {
9899 s[0] = c;
9900 s[1] = '\0';
9901 err |= setvarsafe("OPTARG", s, 0);
9902 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009903 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009904 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009905 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009906 c = '?';
9907 }
Eric Andersenc470f442003-07-28 09:56:35 +00009908 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009909 }
9910
9911 if (p == *optnext)
9912 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009913 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009914 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009915 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009916 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009917 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009918 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009919 *param_optind = optnext - optfirst + 1;
9920 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009921 err |= setvarsafe("OPTIND", s, VNOFUNC);
9922 s[0] = c;
9923 s[1] = '\0';
9924 err |= setvarsafe(optvar, s, 0);
9925 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009926 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009927 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009928 flush_stdout_stderr();
9929 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009930 }
9931 return done;
9932}
Eric Andersenc470f442003-07-28 09:56:35 +00009933
9934/*
9935 * The getopts builtin. Shellparam.optnext points to the next argument
9936 * to be processed. Shellparam.optptr points to the next character to
9937 * be processed in the current argument. If shellparam.optnext is NULL,
9938 * then it's the first time getopts has been called.
9939 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009940static int
Eric Andersenc470f442003-07-28 09:56:35 +00009941getoptscmd(int argc, char **argv)
9942{
9943 char **optbase;
9944
9945 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009946 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009947 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009948 optbase = shellparam.p;
9949 if (shellparam.optind > shellparam.nparam + 1) {
9950 shellparam.optind = 1;
9951 shellparam.optoff = -1;
9952 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009953 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009954 optbase = &argv[3];
9955 if (shellparam.optind > argc - 2) {
9956 shellparam.optind = 1;
9957 shellparam.optoff = -1;
9958 }
9959 }
9960
9961 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009962 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009963}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009964#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009965
Eric Andersencb57d552001-06-28 07:25:16 +00009966
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009967/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009968
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009969struct heredoc {
9970 struct heredoc *next; /* next here document in list */
9971 union node *here; /* redirection node */
9972 char *eofmark; /* string indicating end of input */
9973 smallint striptabs; /* if set, strip leading tabs */
9974};
9975
9976static smallint tokpushback; /* last token pushed back */
9977static smallint parsebackquote; /* nonzero if we are inside backquotes */
9978static smallint quoteflag; /* set if (part of) last token was quoted */
9979static token_id_t lasttoken; /* last token read (integer id Txxx) */
9980static struct heredoc *heredoclist; /* list of here documents to read */
9981static char *wordtext; /* text of last word returned by readtoken */
9982static struct nodelist *backquotelist;
9983static union node *redirnode;
9984static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009985/*
9986 * NEOF is returned by parsecmd when it encounters an end of file. It
9987 * must be distinct from NULL, so we use the address of a variable that
9988 * happens to be handy.
9989 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009990#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009991
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009992static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009993static void
9994raise_error_syntax(const char *msg)
9995{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009996 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009997 /* NOTREACHED */
9998}
9999
10000/*
10001 * Called when an unexpected token is read during the parse. The argument
10002 * is the token that is expected, or -1 if more than one type of token can
10003 * occur at this point.
10004 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010005static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010006static void
10007raise_error_unexpected_syntax(int token)
10008{
10009 char msg[64];
10010 int l;
10011
10012 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10013 if (token >= 0)
10014 sprintf(msg + l, " (expecting %s)", tokname(token));
10015 raise_error_syntax(msg);
10016 /* NOTREACHED */
10017}
Eric Andersencb57d552001-06-28 07:25:16 +000010018
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010019#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010020
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010021/* parsing is heavily cross-recursive, need these forward decls */
10022static union node *andor(void);
10023static union node *pipeline(void);
10024static union node *parse_command(void);
10025static void parseheredoc(void);
10026static char peektoken(void);
10027static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010028
Eric Andersenc470f442003-07-28 09:56:35 +000010029static union node *
10030list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010031{
10032 union node *n1, *n2, *n3;
10033 int tok;
10034
Eric Andersenc470f442003-07-28 09:56:35 +000010035 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10036 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010037 return NULL;
10038 n1 = NULL;
10039 for (;;) {
10040 n2 = andor();
10041 tok = readtoken();
10042 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010043 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010044 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010045 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010046 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010047 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010048 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010049 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010050 n2 = n3;
10051 }
10052 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010053 }
10054 }
10055 if (n1 == NULL) {
10056 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010057 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010058 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010059 n3->type = NSEMI;
10060 n3->nbinary.ch1 = n1;
10061 n3->nbinary.ch2 = n2;
10062 n1 = n3;
10063 }
10064 switch (tok) {
10065 case TBACKGND:
10066 case TSEMI:
10067 tok = readtoken();
10068 /* fall through */
10069 case TNL:
10070 if (tok == TNL) {
10071 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010072 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010073 return n1;
10074 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010075 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010076 }
Eric Andersenc470f442003-07-28 09:56:35 +000010077 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010078 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010079 return n1;
10080 break;
10081 case TEOF:
10082 if (heredoclist)
10083 parseheredoc();
10084 else
Eric Andersenc470f442003-07-28 09:56:35 +000010085 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010086 return n1;
10087 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010088 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010089 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010090 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010091 return n1;
10092 }
10093 }
10094}
10095
Eric Andersenc470f442003-07-28 09:56:35 +000010096static union node *
10097andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010098{
Eric Andersencb57d552001-06-28 07:25:16 +000010099 union node *n1, *n2, *n3;
10100 int t;
10101
Eric Andersencb57d552001-06-28 07:25:16 +000010102 n1 = pipeline();
10103 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010104 t = readtoken();
10105 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010106 t = NAND;
10107 } else if (t == TOR) {
10108 t = NOR;
10109 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010110 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010111 return n1;
10112 }
Eric Andersenc470f442003-07-28 09:56:35 +000010113 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010114 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010115 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010116 n3->type = t;
10117 n3->nbinary.ch1 = n1;
10118 n3->nbinary.ch2 = n2;
10119 n1 = n3;
10120 }
10121}
10122
Eric Andersenc470f442003-07-28 09:56:35 +000010123static union node *
10124pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010125{
Eric Andersencb57d552001-06-28 07:25:16 +000010126 union node *n1, *n2, *pipenode;
10127 struct nodelist *lp, *prev;
10128 int negate;
10129
10130 negate = 0;
10131 TRACE(("pipeline: entered\n"));
10132 if (readtoken() == TNOT) {
10133 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010134 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010135 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010136 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010137 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010138 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010139 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010140 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010141 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010142 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010143 pipenode->npipe.cmdlist = lp;
10144 lp->n = n1;
10145 do {
10146 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010147 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010148 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010149 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010150 prev->next = lp;
10151 } while (readtoken() == TPIPE);
10152 lp->next = NULL;
10153 n1 = pipenode;
10154 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010155 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010156 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010157 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010158 n2->type = NNOT;
10159 n2->nnot.com = n1;
10160 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010161 }
10162 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010163}
10164
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010165static union node *
10166makename(void)
10167{
10168 union node *n;
10169
Denis Vlasenko597906c2008-02-20 16:38:54 +000010170 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010171 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010172 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010173 n->narg.text = wordtext;
10174 n->narg.backquote = backquotelist;
10175 return n;
10176}
10177
10178static void
10179fixredir(union node *n, const char *text, int err)
10180{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010181 int fd;
10182
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010183 TRACE(("Fix redir %s %d\n", text, err));
10184 if (!err)
10185 n->ndup.vname = NULL;
10186
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010187 fd = bb_strtou(text, NULL, 10);
10188 if (!errno && fd >= 0)
10189 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010190 else if (LONE_DASH(text))
10191 n->ndup.dupfd = -1;
10192 else {
10193 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010194 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010195 n->ndup.vname = makename();
10196 }
10197}
10198
10199/*
10200 * Returns true if the text contains nothing to expand (no dollar signs
10201 * or backquotes).
10202 */
10203static int
10204noexpand(char *text)
10205{
10206 char *p;
10207 char c;
10208
10209 p = text;
10210 while ((c = *p++) != '\0') {
10211 if (c == CTLQUOTEMARK)
10212 continue;
10213 if (c == CTLESC)
10214 p++;
10215 else if (SIT(c, BASESYNTAX) == CCTL)
10216 return 0;
10217 }
10218 return 1;
10219}
10220
10221static void
10222parsefname(void)
10223{
10224 union node *n = redirnode;
10225
10226 if (readtoken() != TWORD)
10227 raise_error_unexpected_syntax(-1);
10228 if (n->type == NHERE) {
10229 struct heredoc *here = heredoc;
10230 struct heredoc *p;
10231 int i;
10232
10233 if (quoteflag == 0)
10234 n->type = NXHERE;
10235 TRACE(("Here document %d\n", n->type));
10236 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010237 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010238 rmescapes(wordtext);
10239 here->eofmark = wordtext;
10240 here->next = NULL;
10241 if (heredoclist == NULL)
10242 heredoclist = here;
10243 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010244 for (p = heredoclist; p->next; p = p->next)
10245 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010246 p->next = here;
10247 }
10248 } else if (n->type == NTOFD || n->type == NFROMFD) {
10249 fixredir(n, wordtext, 0);
10250 } else {
10251 n->nfile.fname = makename();
10252 }
10253}
Eric Andersencb57d552001-06-28 07:25:16 +000010254
Eric Andersenc470f442003-07-28 09:56:35 +000010255static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010256simplecmd(void)
10257{
10258 union node *args, **app;
10259 union node *n = NULL;
10260 union node *vars, **vpp;
10261 union node **rpp, *redir;
10262 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010263#if ENABLE_ASH_BASH_COMPAT
10264 smallint double_brackets_flag = 0;
10265#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010266
10267 args = NULL;
10268 app = &args;
10269 vars = NULL;
10270 vpp = &vars;
10271 redir = NULL;
10272 rpp = &redir;
10273
10274 savecheckkwd = CHKALIAS;
10275 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010276 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010277 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010278 t = readtoken();
10279 switch (t) {
10280#if ENABLE_ASH_BASH_COMPAT
10281 case TAND: /* "&&" */
10282 case TOR: /* "||" */
10283 if (!double_brackets_flag) {
10284 tokpushback = 1;
10285 goto out;
10286 }
10287 wordtext = (char *) (t == TAND ? "-a" : "-o");
10288#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010289 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010290 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010291 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010292 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010293 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010294#if ENABLE_ASH_BASH_COMPAT
10295 if (strcmp("[[", wordtext) == 0)
10296 double_brackets_flag = 1;
10297 else if (strcmp("]]", wordtext) == 0)
10298 double_brackets_flag = 0;
10299#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010300 n->narg.backquote = backquotelist;
10301 if (savecheckkwd && isassignment(wordtext)) {
10302 *vpp = n;
10303 vpp = &n->narg.next;
10304 } else {
10305 *app = n;
10306 app = &n->narg.next;
10307 savecheckkwd = 0;
10308 }
10309 break;
10310 case TREDIR:
10311 *rpp = n = redirnode;
10312 rpp = &n->nfile.next;
10313 parsefname(); /* read name of redirection file */
10314 break;
10315 case TLP:
10316 if (args && app == &args->narg.next
10317 && !vars && !redir
10318 ) {
10319 struct builtincmd *bcmd;
10320 const char *name;
10321
10322 /* We have a function */
10323 if (readtoken() != TRP)
10324 raise_error_unexpected_syntax(TRP);
10325 name = n->narg.text;
10326 if (!goodname(name)
10327 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10328 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010329 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010330 }
10331 n->type = NDEFUN;
10332 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10333 n->narg.next = parse_command();
10334 return n;
10335 }
10336 /* fall through */
10337 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010338 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010339 goto out;
10340 }
10341 }
10342 out:
10343 *app = NULL;
10344 *vpp = NULL;
10345 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010346 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010347 n->type = NCMD;
10348 n->ncmd.args = args;
10349 n->ncmd.assign = vars;
10350 n->ncmd.redirect = redir;
10351 return n;
10352}
10353
10354static union node *
10355parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010356{
Eric Andersencb57d552001-06-28 07:25:16 +000010357 union node *n1, *n2;
10358 union node *ap, **app;
10359 union node *cp, **cpp;
10360 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010361 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010362 int t;
10363
10364 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010365 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010366
Eric Andersencb57d552001-06-28 07:25:16 +000010367 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010368 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010369 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010370 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010371 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010372 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010373 n1->type = NIF;
10374 n1->nif.test = list(0);
10375 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010376 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010377 n1->nif.ifpart = list(0);
10378 n2 = n1;
10379 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010380 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010381 n2 = n2->nif.elsepart;
10382 n2->type = NIF;
10383 n2->nif.test = list(0);
10384 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010385 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010386 n2->nif.ifpart = list(0);
10387 }
10388 if (lasttoken == TELSE)
10389 n2->nif.elsepart = list(0);
10390 else {
10391 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010392 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010393 }
Eric Andersenc470f442003-07-28 09:56:35 +000010394 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010395 break;
10396 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010397 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010398 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010399 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010400 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010401 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010402 got = readtoken();
10403 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010404 TRACE(("expecting DO got %s %s\n", tokname(got),
10405 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010406 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010407 }
10408 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010409 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010410 break;
10411 }
10412 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010413 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010414 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010415 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010416 n1->type = NFOR;
10417 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010418 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010419 if (readtoken() == TIN) {
10420 app = &ap;
10421 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010422 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010423 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010424 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010425 n2->narg.text = wordtext;
10426 n2->narg.backquote = backquotelist;
10427 *app = n2;
10428 app = &n2->narg.next;
10429 }
10430 *app = NULL;
10431 n1->nfor.args = ap;
10432 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010433 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010434 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010435 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010436 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010437 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010438 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010439 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010440 n1->nfor.args = n2;
10441 /*
10442 * Newline or semicolon here is optional (but note
10443 * that the original Bourne shell only allowed NL).
10444 */
10445 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010446 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010447 }
Eric Andersenc470f442003-07-28 09:56:35 +000010448 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010449 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010450 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010451 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010452 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010453 break;
10454 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010455 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010456 n1->type = NCASE;
10457 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010458 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010459 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010460 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010461 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010462 n2->narg.text = wordtext;
10463 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010464 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010465 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010466 } while (readtoken() == TNL);
10467 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010468 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010469 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010470 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010471 checkkwd = CHKNL | CHKKWD;
10472 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010473 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010474 if (lasttoken == TLP)
10475 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010476 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010477 cp->type = NCLIST;
10478 app = &cp->nclist.pattern;
10479 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010480 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010481 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010482 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010483 ap->narg.text = wordtext;
10484 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010485 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010486 break;
10487 app = &ap->narg.next;
10488 readtoken();
10489 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010490 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010491 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010492 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010493 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010494
Eric Andersenc470f442003-07-28 09:56:35 +000010495 cpp = &cp->nclist.next;
10496
10497 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010498 t = readtoken();
10499 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010500 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010501 raise_error_unexpected_syntax(TENDCASE);
10502 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010503 }
Eric Andersenc470f442003-07-28 09:56:35 +000010504 }
Eric Andersencb57d552001-06-28 07:25:16 +000010505 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010506 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010507 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010508 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010509 n1->type = NSUBSHELL;
10510 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010511 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010512 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010513 break;
10514 case TBEGIN:
10515 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010516 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010517 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010518 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010519 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010520 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010521 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010522 }
10523
Eric Andersenc470f442003-07-28 09:56:35 +000010524 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010525 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010526
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010527 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010528 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010529 checkkwd = CHKKWD | CHKALIAS;
10530 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010531 while (readtoken() == TREDIR) {
10532 *rpp = n2 = redirnode;
10533 rpp = &n2->nfile.next;
10534 parsefname();
10535 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010536 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010537 *rpp = NULL;
10538 if (redir) {
10539 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010540 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010541 n2->type = NREDIR;
10542 n2->nredir.n = n1;
10543 n1 = n2;
10544 }
10545 n1->nredir.redirect = redir;
10546 }
Eric Andersencb57d552001-06-28 07:25:16 +000010547 return n1;
10548}
10549
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010550#if ENABLE_ASH_BASH_COMPAT
10551static int decode_dollar_squote(void)
10552{
10553 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10554 int c, cnt;
10555 char *p;
10556 char buf[4];
10557
10558 c = pgetc();
10559 p = strchr(C_escapes, c);
10560 if (p) {
10561 buf[0] = c;
10562 p = buf;
10563 cnt = 3;
10564 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10565 do {
10566 c = pgetc();
10567 *++p = c;
10568 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10569 pungetc();
10570 } else if (c == 'x') { /* \xHH */
10571 do {
10572 c = pgetc();
10573 *++p = c;
10574 } while (isxdigit(c) && --cnt);
10575 pungetc();
10576 if (cnt == 3) { /* \x but next char is "bad" */
10577 c = 'x';
10578 goto unrecognized;
10579 }
10580 } else { /* simple seq like \\ or \t */
10581 p++;
10582 }
10583 *p = '\0';
10584 p = buf;
10585 c = bb_process_escape_sequence((void*)&p);
10586 } else { /* unrecognized "\z": print both chars unless ' or " */
10587 if (c != '\'' && c != '"') {
10588 unrecognized:
10589 c |= 0x100; /* "please encode \, then me" */
10590 }
10591 }
10592 return c;
10593}
10594#endif
10595
Eric Andersencb57d552001-06-28 07:25:16 +000010596/*
10597 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10598 * is not NULL, read a here document. In the latter case, eofmark is the
10599 * word which marks the end of the document and striptabs is true if
10600 * leading tabs should be stripped from the document. The argument firstc
10601 * is the first character of the input token or document.
10602 *
10603 * Because C does not have internal subroutines, I have simulated them
10604 * using goto's to implement the subroutine linkage. The following macros
10605 * will run code that appears at the end of readtoken1.
10606 */
Eric Andersen2870d962001-07-02 17:27:21 +000010607#define CHECKEND() {goto checkend; checkend_return:;}
10608#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10609#define PARSESUB() {goto parsesub; parsesub_return:;}
10610#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10611#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10612#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010613static int
Eric Andersenc470f442003-07-28 09:56:35 +000010614readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010615{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010616 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010617 int c = firstc;
10618 char *out;
10619 int len;
10620 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010621 struct nodelist *bqlist;
10622 smallint quotef;
10623 smallint dblquote;
10624 smallint oldstyle;
10625 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010626#if ENABLE_ASH_EXPAND_PRMT
10627 smallint pssyntax; /* we are expanding a prompt string */
10628#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010629 int varnest; /* levels of variables expansion */
10630 int arinest; /* levels of arithmetic expansion */
10631 int parenlevel; /* levels of parens in arithmetic */
10632 int dqvarnest; /* levels of variables expansion within double quotes */
10633
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010634 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10635
Eric Andersencb57d552001-06-28 07:25:16 +000010636#if __GNUC__
10637 /* Avoid longjmp clobbering */
10638 (void) &out;
10639 (void) &quotef;
10640 (void) &dblquote;
10641 (void) &varnest;
10642 (void) &arinest;
10643 (void) &parenlevel;
10644 (void) &dqvarnest;
10645 (void) &oldstyle;
10646 (void) &prevsyntax;
10647 (void) &syntax;
10648#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010649 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010650 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010651 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010652 oldstyle = 0;
10653 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010654#if ENABLE_ASH_EXPAND_PRMT
10655 pssyntax = (syntax == PSSYNTAX);
10656 if (pssyntax)
10657 syntax = DQSYNTAX;
10658#endif
10659 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010660 varnest = 0;
10661 arinest = 0;
10662 parenlevel = 0;
10663 dqvarnest = 0;
10664
10665 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010666 loop: { /* for each line, until end of word */
10667 CHECKEND(); /* set c to PEOF if at end of here document */
10668 for (;;) { /* until end of line or end of word */
10669 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010670 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010671 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010672 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010673 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010674 USTPUTC(c, out);
10675 plinno++;
10676 if (doprompt)
10677 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010678 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010679 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010680 case CWORD:
10681 USTPUTC(c, out);
10682 break;
10683 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010684 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010685 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010686#if ENABLE_ASH_BASH_COMPAT
10687 if (c == '\\' && bash_dollar_squote) {
10688 c = decode_dollar_squote();
10689 if (c & 0x100) {
10690 USTPUTC('\\', out);
10691 c = (unsigned char)c;
10692 }
10693 }
10694#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010695 USTPUTC(c, out);
10696 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010697 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010698 c = pgetc2();
10699 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010700 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010701 USTPUTC('\\', out);
10702 pungetc();
10703 } else if (c == '\n') {
10704 if (doprompt)
10705 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010706 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010707#if ENABLE_ASH_EXPAND_PRMT
10708 if (c == '$' && pssyntax) {
10709 USTPUTC(CTLESC, out);
10710 USTPUTC('\\', out);
10711 }
10712#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010713 if (dblquote && c != '\\'
10714 && c != '`' && c != '$'
10715 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010716 ) {
10717 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010718 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010719 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010720 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010721 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010722 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010723 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010724 }
10725 break;
10726 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010727 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010728 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010729 if (eofmark == NULL) {
10730 USTPUTC(CTLQUOTEMARK, out);
10731 }
Eric Andersencb57d552001-06-28 07:25:16 +000010732 break;
10733 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010734 syntax = DQSYNTAX;
10735 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010736 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010737 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010738 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010739 if (eofmark != NULL && arinest == 0
10740 && varnest == 0
10741 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010742 USTPUTC(c, out);
10743 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010744 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010745 syntax = BASESYNTAX;
10746 dblquote = 0;
10747 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010748 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010749 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010750 }
10751 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010752 case CVAR: /* '$' */
10753 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010754 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010755 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010756 if (varnest > 0) {
10757 varnest--;
10758 if (dqvarnest > 0) {
10759 dqvarnest--;
10760 }
10761 USTPUTC(CTLENDVAR, out);
10762 } else {
10763 USTPUTC(c, out);
10764 }
10765 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010766#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010767 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010768 parenlevel++;
10769 USTPUTC(c, out);
10770 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010771 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010772 if (parenlevel > 0) {
10773 USTPUTC(c, out);
10774 --parenlevel;
10775 } else {
10776 if (pgetc() == ')') {
10777 if (--arinest == 0) {
10778 USTPUTC(CTLENDARI, out);
10779 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010780 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010781 } else
10782 USTPUTC(')', out);
10783 } else {
10784 /*
10785 * unbalanced parens
10786 * (don't 2nd guess - no error)
10787 */
10788 pungetc();
10789 USTPUTC(')', out);
10790 }
10791 }
10792 break;
10793#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010794 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010795 PARSEBACKQOLD();
10796 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010797 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010798 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010799 case CIGN:
10800 break;
10801 default:
10802 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010803 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010804#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010805 if (c != PEOA)
10806#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010807 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010808
Eric Andersencb57d552001-06-28 07:25:16 +000010809 }
10810 c = pgetc_macro();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010811 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010812 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010813 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010814#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010815 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010816 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010817#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010818 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010819 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010820 if (varnest != 0) {
10821 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010822 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010823 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010824 }
10825 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010826 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010827 out = stackblock();
10828 if (eofmark == NULL) {
Denis Vlasenko5a867312008-07-24 19:46:38 +000010829 if ((c == '>' || c == '<') && quotef == 0) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010830 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010831 PARSEREDIR(); /* passed as params: out, c */
10832 lasttoken = TREDIR;
10833 return lasttoken;
10834 }
10835 /* else: non-number X seen, interpret it
10836 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010837 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010838 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010839 }
10840 quoteflag = quotef;
10841 backquotelist = bqlist;
10842 grabstackblock(len);
10843 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010844 lasttoken = TWORD;
10845 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010846/* end of readtoken routine */
10847
Eric Andersencb57d552001-06-28 07:25:16 +000010848/*
10849 * Check to see whether we are at the end of the here document. When this
10850 * is called, c is set to the first character of the next input line. If
10851 * we are at the end of the here document, this routine sets the c to PEOF.
10852 */
Eric Andersenc470f442003-07-28 09:56:35 +000010853checkend: {
10854 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010855#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010856 if (c == PEOA) {
10857 c = pgetc2();
10858 }
10859#endif
10860 if (striptabs) {
10861 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010862 c = pgetc2();
10863 }
Eric Andersenc470f442003-07-28 09:56:35 +000010864 }
10865 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010866 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010867 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010868
Eric Andersenc470f442003-07-28 09:56:35 +000010869 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010870 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10871 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010872 if (*p == '\n' && *q == '\0') {
10873 c = PEOF;
10874 plinno++;
10875 needprompt = doprompt;
10876 } else {
10877 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010878 }
10879 }
10880 }
10881 }
Eric Andersenc470f442003-07-28 09:56:35 +000010882 goto checkend_return;
10883}
Eric Andersencb57d552001-06-28 07:25:16 +000010884
Eric Andersencb57d552001-06-28 07:25:16 +000010885/*
10886 * Parse a redirection operator. The variable "out" points to a string
10887 * specifying the fd to be redirected. The variable "c" contains the
10888 * first character of the redirection operator.
10889 */
Eric Andersenc470f442003-07-28 09:56:35 +000010890parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010891 /* out is already checked to be a valid number or "" */
10892 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010893 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010894
Denis Vlasenko597906c2008-02-20 16:38:54 +000010895 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010896 if (c == '>') {
10897 np->nfile.fd = 1;
10898 c = pgetc();
10899 if (c == '>')
10900 np->type = NAPPEND;
10901 else if (c == '|')
10902 np->type = NCLOBBER;
10903 else if (c == '&')
10904 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010905 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010906 else {
10907 np->type = NTO;
10908 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010909 }
Eric Andersenc470f442003-07-28 09:56:35 +000010910 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010911 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010912 c = pgetc();
10913 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010914 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010915 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010916 np = stzalloc(sizeof(struct nhere));
10917 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010918 }
10919 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010920 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010921 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010922 c = pgetc();
10923 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010924 heredoc->striptabs = 1;
10925 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010926 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010927 pungetc();
10928 }
10929 break;
10930
10931 case '&':
10932 np->type = NFROMFD;
10933 break;
10934
10935 case '>':
10936 np->type = NFROMTO;
10937 break;
10938
10939 default:
10940 np->type = NFROM;
10941 pungetc();
10942 break;
10943 }
Eric Andersencb57d552001-06-28 07:25:16 +000010944 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010945 if (fd >= 0)
10946 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010947 redirnode = np;
10948 goto parseredir_return;
10949}
Eric Andersencb57d552001-06-28 07:25:16 +000010950
Eric Andersencb57d552001-06-28 07:25:16 +000010951/*
10952 * Parse a substitution. At this point, we have read the dollar sign
10953 * and nothing else.
10954 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010955
10956/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10957 * (assuming ascii char codes, as the original implementation did) */
10958#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010959 (((unsigned)(c) - 33 < 32) \
10960 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010961parsesub: {
10962 int subtype;
10963 int typeloc;
10964 int flags;
10965 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010966 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010967
Eric Andersenc470f442003-07-28 09:56:35 +000010968 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010969 if (c <= PEOA_OR_PEOF
10970 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010971 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010972#if ENABLE_ASH_BASH_COMPAT
10973 if (c == '\'')
10974 bash_dollar_squote = 1;
10975 else
10976#endif
10977 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010978 pungetc();
10979 } else if (c == '(') { /* $(command) or $((arith)) */
10980 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010981#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010982 PARSEARITH();
10983#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010984 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010985#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010986 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010987 pungetc();
10988 PARSEBACKQNEW();
10989 }
10990 } else {
10991 USTPUTC(CTLVAR, out);
10992 typeloc = out - (char *)stackblock();
10993 USTPUTC(VSNORMAL, out);
10994 subtype = VSNORMAL;
10995 if (c == '{') {
10996 c = pgetc();
10997 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010998 c = pgetc();
10999 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011000 c = '#';
11001 else
11002 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011003 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011004 subtype = 0;
11005 }
11006 if (c > PEOA_OR_PEOF && is_name(c)) {
11007 do {
11008 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011009 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011010 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011011 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011012 do {
11013 STPUTC(c, out);
11014 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011015 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011016 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011017 USTPUTC(c, out);
11018 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011019 } else {
11020 badsub:
11021 raise_error_syntax("bad substitution");
11022 }
Eric Andersencb57d552001-06-28 07:25:16 +000011023
Eric Andersenc470f442003-07-28 09:56:35 +000011024 STPUTC('=', out);
11025 flags = 0;
11026 if (subtype == 0) {
11027 switch (c) {
11028 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011029 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011030#if ENABLE_ASH_BASH_COMPAT
11031 if (c == ':' || c == '$' || isdigit(c)) {
11032 pungetc();
11033 subtype = VSSUBSTR;
11034 break;
11035 }
11036#endif
11037 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011038 /*FALLTHROUGH*/
11039 default:
11040 p = strchr(types, c);
11041 if (p == NULL)
11042 goto badsub;
11043 subtype = p - types + VSNORMAL;
11044 break;
11045 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011046 case '#': {
11047 int cc = c;
11048 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11049 c = pgetc();
11050 if (c == cc)
11051 subtype++;
11052 else
11053 pungetc();
11054 break;
11055 }
11056#if ENABLE_ASH_BASH_COMPAT
11057 case '/':
11058 subtype = VSREPLACE;
11059 c = pgetc();
11060 if (c == '/')
11061 subtype++; /* VSREPLACEALL */
11062 else
11063 pungetc();
11064 break;
11065#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011066 }
Eric Andersenc470f442003-07-28 09:56:35 +000011067 } else {
11068 pungetc();
11069 }
11070 if (dblquote || arinest)
11071 flags |= VSQUOTE;
11072 *((char *)stackblock() + typeloc) = subtype | flags;
11073 if (subtype != VSNORMAL) {
11074 varnest++;
11075 if (dblquote || arinest) {
11076 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011077 }
11078 }
11079 }
Eric Andersenc470f442003-07-28 09:56:35 +000011080 goto parsesub_return;
11081}
Eric Andersencb57d552001-06-28 07:25:16 +000011082
Eric Andersencb57d552001-06-28 07:25:16 +000011083/*
11084 * Called to parse command substitutions. Newstyle is set if the command
11085 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11086 * list of commands (passed by reference), and savelen is the number of
11087 * characters on the top of the stack which must be preserved.
11088 */
Eric Andersenc470f442003-07-28 09:56:35 +000011089parsebackq: {
11090 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011091 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011092 union node *n;
11093 char *volatile str;
11094 struct jmploc jmploc;
11095 struct jmploc *volatile savehandler;
11096 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011097 smallint saveprompt = 0;
11098
Eric Andersencb57d552001-06-28 07:25:16 +000011099#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011100 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011101#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011102 savepbq = parsebackquote;
11103 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011104 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011105 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011106 exception_handler = savehandler;
11107 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011108 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011109 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011110 str = NULL;
11111 savelen = out - (char *)stackblock();
11112 if (savelen > 0) {
11113 str = ckmalloc(savelen);
11114 memcpy(str, stackblock(), savelen);
11115 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011116 savehandler = exception_handler;
11117 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011118 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011119 if (oldstyle) {
11120 /* We must read until the closing backquote, giving special
11121 treatment to some slashes, and then push the string and
11122 reread it as input, interpreting it normally. */
11123 char *pout;
11124 int pc;
11125 size_t psavelen;
11126 char *pstr;
11127
11128
11129 STARTSTACKSTR(pout);
11130 for (;;) {
11131 if (needprompt) {
11132 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011133 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011134 pc = pgetc();
11135 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011136 case '`':
11137 goto done;
11138
11139 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011140 pc = pgetc();
11141 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000011142 plinno++;
11143 if (doprompt)
11144 setprompt(2);
11145 /*
11146 * If eating a newline, avoid putting
11147 * the newline into the new character
11148 * stream (via the STPUTC after the
11149 * switch).
11150 */
11151 continue;
11152 }
11153 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011154 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011155 STPUTC('\\', pout);
11156 if (pc > PEOA_OR_PEOF) {
11157 break;
11158 }
11159 /* fall through */
11160
11161 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011162#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011163 case PEOA:
11164#endif
11165 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011166 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011167
11168 case '\n':
11169 plinno++;
11170 needprompt = doprompt;
11171 break;
11172
11173 default:
11174 break;
11175 }
11176 STPUTC(pc, pout);
11177 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011178 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011179 STPUTC('\0', pout);
11180 psavelen = pout - (char *)stackblock();
11181 if (psavelen > 0) {
11182 pstr = grabstackstr(pout);
11183 setinputstring(pstr);
11184 }
11185 }
11186 nlpp = &bqlist;
11187 while (*nlpp)
11188 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011189 *nlpp = stzalloc(sizeof(**nlpp));
11190 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011191 parsebackquote = oldstyle;
11192
11193 if (oldstyle) {
11194 saveprompt = doprompt;
11195 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011196 }
11197
Eric Andersenc470f442003-07-28 09:56:35 +000011198 n = list(2);
11199
11200 if (oldstyle)
11201 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011202 else if (readtoken() != TRP)
11203 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011204
11205 (*nlpp)->n = n;
11206 if (oldstyle) {
11207 /*
11208 * Start reading from old file again, ignoring any pushed back
11209 * tokens left from the backquote parsing
11210 */
11211 popfile();
11212 tokpushback = 0;
11213 }
11214 while (stackblocksize() <= savelen)
11215 growstackblock();
11216 STARTSTACKSTR(out);
11217 if (str) {
11218 memcpy(out, str, savelen);
11219 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011220 INT_OFF;
11221 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011222 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011223 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011224 }
11225 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011226 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011227 if (arinest || dblquote)
11228 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11229 else
11230 USTPUTC(CTLBACKQ, out);
11231 if (oldstyle)
11232 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011233 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011234}
11235
Denis Vlasenko131ae172007-02-18 13:00:19 +000011236#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011237/*
11238 * Parse an arithmetic expansion (indicate start of one and set state)
11239 */
Eric Andersenc470f442003-07-28 09:56:35 +000011240parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011241 if (++arinest == 1) {
11242 prevsyntax = syntax;
11243 syntax = ARISYNTAX;
11244 USTPUTC(CTLARI, out);
11245 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011246 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011247 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011248 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011249 } else {
11250 /*
11251 * we collapse embedded arithmetic expansion to
11252 * parenthesis, which should be equivalent
11253 */
11254 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011255 }
Eric Andersenc470f442003-07-28 09:56:35 +000011256 goto parsearith_return;
11257}
11258#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011259
Eric Andersenc470f442003-07-28 09:56:35 +000011260} /* end of readtoken */
11261
Eric Andersencb57d552001-06-28 07:25:16 +000011262/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011263 * Read the next input token.
11264 * If the token is a word, we set backquotelist to the list of cmds in
11265 * backquotes. We set quoteflag to true if any part of the word was
11266 * quoted.
11267 * If the token is TREDIR, then we set redirnode to a structure containing
11268 * the redirection.
11269 * In all cases, the variable startlinno is set to the number of the line
11270 * on which the token starts.
11271 *
11272 * [Change comment: here documents and internal procedures]
11273 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11274 * word parsing code into a separate routine. In this case, readtoken
11275 * doesn't need to have any internal procedures, but parseword does.
11276 * We could also make parseoperator in essence the main routine, and
11277 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011278 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011279#define NEW_xxreadtoken
11280#ifdef NEW_xxreadtoken
11281/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011282static const char xxreadtoken_chars[7] ALIGN1 = {
11283 '\n', '(', ')', '&', '|', ';', 0
11284};
Eric Andersencb57d552001-06-28 07:25:16 +000011285
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011286static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011287 TNL, TLP, TRP, /* only single occurrence allowed */
11288 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11289 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011290 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011291};
11292
11293#define xxreadtoken_doubles \
11294 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11295#define xxreadtoken_singles \
11296 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11297
11298static int
11299xxreadtoken(void)
11300{
11301 int c;
11302
11303 if (tokpushback) {
11304 tokpushback = 0;
11305 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011306 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011307 if (needprompt) {
11308 setprompt(2);
11309 }
11310 startlinno = plinno;
11311 for (;;) { /* until token or start of word found */
11312 c = pgetc_macro();
11313
11314 if ((c != ' ') && (c != '\t')
11315#if ENABLE_ASH_ALIAS
11316 && (c != PEOA)
11317#endif
11318 ) {
11319 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011320 while ((c = pgetc()) != '\n' && c != PEOF)
11321 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011322 pungetc();
11323 } else if (c == '\\') {
11324 if (pgetc() != '\n') {
11325 pungetc();
11326 goto READTOKEN1;
11327 }
11328 startlinno = ++plinno;
11329 if (doprompt)
11330 setprompt(2);
11331 } else {
11332 const char *p
11333 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11334
11335 if (c != PEOF) {
11336 if (c == '\n') {
11337 plinno++;
11338 needprompt = doprompt;
11339 }
11340
11341 p = strchr(xxreadtoken_chars, c);
11342 if (p == NULL) {
11343 READTOKEN1:
11344 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11345 }
11346
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011347 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011348 if (pgetc() == *p) { /* double occurrence? */
11349 p += xxreadtoken_doubles + 1;
11350 } else {
11351 pungetc();
11352 }
11353 }
11354 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011355 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11356 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011357 }
11358 }
11359 } /* for */
11360}
11361#else
11362#define RETURN(token) return lasttoken = token
11363static int
11364xxreadtoken(void)
11365{
11366 int c;
11367
11368 if (tokpushback) {
11369 tokpushback = 0;
11370 return lasttoken;
11371 }
11372 if (needprompt) {
11373 setprompt(2);
11374 }
11375 startlinno = plinno;
11376 for (;;) { /* until token or start of word found */
11377 c = pgetc_macro();
11378 switch (c) {
11379 case ' ': case '\t':
11380#if ENABLE_ASH_ALIAS
11381 case PEOA:
11382#endif
11383 continue;
11384 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011385 while ((c = pgetc()) != '\n' && c != PEOF)
11386 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011387 pungetc();
11388 continue;
11389 case '\\':
11390 if (pgetc() == '\n') {
11391 startlinno = ++plinno;
11392 if (doprompt)
11393 setprompt(2);
11394 continue;
11395 }
11396 pungetc();
11397 goto breakloop;
11398 case '\n':
11399 plinno++;
11400 needprompt = doprompt;
11401 RETURN(TNL);
11402 case PEOF:
11403 RETURN(TEOF);
11404 case '&':
11405 if (pgetc() == '&')
11406 RETURN(TAND);
11407 pungetc();
11408 RETURN(TBACKGND);
11409 case '|':
11410 if (pgetc() == '|')
11411 RETURN(TOR);
11412 pungetc();
11413 RETURN(TPIPE);
11414 case ';':
11415 if (pgetc() == ';')
11416 RETURN(TENDCASE);
11417 pungetc();
11418 RETURN(TSEMI);
11419 case '(':
11420 RETURN(TLP);
11421 case ')':
11422 RETURN(TRP);
11423 default:
11424 goto breakloop;
11425 }
11426 }
11427 breakloop:
11428 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11429#undef RETURN
11430}
11431#endif /* NEW_xxreadtoken */
11432
11433static int
11434readtoken(void)
11435{
11436 int t;
11437#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011438 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011439#endif
11440
11441#if ENABLE_ASH_ALIAS
11442 top:
11443#endif
11444
11445 t = xxreadtoken();
11446
11447 /*
11448 * eat newlines
11449 */
11450 if (checkkwd & CHKNL) {
11451 while (t == TNL) {
11452 parseheredoc();
11453 t = xxreadtoken();
11454 }
11455 }
11456
11457 if (t != TWORD || quoteflag) {
11458 goto out;
11459 }
11460
11461 /*
11462 * check for keywords
11463 */
11464 if (checkkwd & CHKKWD) {
11465 const char *const *pp;
11466
11467 pp = findkwd(wordtext);
11468 if (pp) {
11469 lasttoken = t = pp - tokname_array;
11470 TRACE(("keyword %s recognized\n", tokname(t)));
11471 goto out;
11472 }
11473 }
11474
11475 if (checkkwd & CHKALIAS) {
11476#if ENABLE_ASH_ALIAS
11477 struct alias *ap;
11478 ap = lookupalias(wordtext, 1);
11479 if (ap != NULL) {
11480 if (*ap->val) {
11481 pushstring(ap->val, ap);
11482 }
11483 goto top;
11484 }
11485#endif
11486 }
11487 out:
11488 checkkwd = 0;
11489#if DEBUG
11490 if (!alreadyseen)
11491 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11492 else
11493 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11494#endif
11495 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011496}
11497
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011498static char
11499peektoken(void)
11500{
11501 int t;
11502
11503 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011504 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011505 return tokname_array[t][0];
11506}
Eric Andersencb57d552001-06-28 07:25:16 +000011507
11508/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011509 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11510 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011511 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011512static union node *
11513parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011514{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011515 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011516
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011517 tokpushback = 0;
11518 doprompt = interact;
11519 if (doprompt)
11520 setprompt(doprompt);
11521 needprompt = 0;
11522 t = readtoken();
11523 if (t == TEOF)
11524 return NEOF;
11525 if (t == TNL)
11526 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011527 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011528 return list(1);
11529}
11530
11531/*
11532 * Input any here documents.
11533 */
11534static void
11535parseheredoc(void)
11536{
11537 struct heredoc *here;
11538 union node *n;
11539
11540 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011541 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011542
11543 while (here) {
11544 if (needprompt) {
11545 setprompt(2);
11546 }
11547 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11548 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011549 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011550 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011551 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011552 n->narg.text = wordtext;
11553 n->narg.backquote = backquotelist;
11554 here->here->nhere.doc = n;
11555 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011556 }
Eric Andersencb57d552001-06-28 07:25:16 +000011557}
11558
11559
11560/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011561 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011562 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011563#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011564static const char *
11565expandstr(const char *ps)
11566{
11567 union node n;
11568
11569 /* XXX Fix (char *) cast. */
11570 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011571 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011572 popfile();
11573
11574 n.narg.type = NARG;
11575 n.narg.next = NULL;
11576 n.narg.text = wordtext;
11577 n.narg.backquote = backquotelist;
11578
11579 expandarg(&n, NULL, 0);
11580 return stackblock();
11581}
11582#endif
11583
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011584/*
11585 * Execute a command or commands contained in a string.
11586 */
11587static int
11588evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011589{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011590 union node *n;
11591 struct stackmark smark;
11592 int skip;
11593
11594 setinputstring(s);
11595 setstackmark(&smark);
11596
11597 skip = 0;
11598 while ((n = parsecmd(0)) != NEOF) {
11599 evaltree(n, 0);
11600 popstackmark(&smark);
11601 skip = evalskip;
11602 if (skip)
11603 break;
11604 }
11605 popfile();
11606
11607 skip &= mask;
11608 evalskip = skip;
11609 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011610}
11611
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011612/*
11613 * The eval command.
11614 */
11615static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011616evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011617{
11618 char *p;
11619 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011620
Denis Vlasenko68404f12008-03-17 09:00:54 +000011621 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011622 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011623 argv += 2;
11624 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011625 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011626 for (;;) {
11627 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011628 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011629 if (p == NULL)
11630 break;
11631 STPUTC(' ', concat);
11632 }
11633 STPUTC('\0', concat);
11634 p = grabstackstr(concat);
11635 }
11636 evalstring(p, ~SKIPEVAL);
11637
11638 }
11639 return exitstatus;
11640}
11641
11642/*
11643 * Read and execute commands. "Top" is nonzero for the top level command
11644 * loop; it turns on prompting if the shell is interactive.
11645 */
11646static int
11647cmdloop(int top)
11648{
11649 union node *n;
11650 struct stackmark smark;
11651 int inter;
11652 int numeof = 0;
11653
11654 TRACE(("cmdloop(%d) called\n", top));
11655 for (;;) {
11656 int skip;
11657
11658 setstackmark(&smark);
11659#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011660 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011661 showjobs(stderr, SHOW_CHANGED);
11662#endif
11663 inter = 0;
11664 if (iflag && top) {
11665 inter++;
11666#if ENABLE_ASH_MAIL
11667 chkmail();
11668#endif
11669 }
11670 n = parsecmd(inter);
11671 /* showtree(n); DEBUG */
11672 if (n == NEOF) {
11673 if (!top || numeof >= 50)
11674 break;
11675 if (!stoppedjobs()) {
11676 if (!Iflag)
11677 break;
11678 out2str("\nUse \"exit\" to leave shell.\n");
11679 }
11680 numeof++;
11681 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011682 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11683 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011684 numeof = 0;
11685 evaltree(n, 0);
11686 }
11687 popstackmark(&smark);
11688 skip = evalskip;
11689
11690 if (skip) {
11691 evalskip = 0;
11692 return skip & SKIPEVAL;
11693 }
11694 }
11695 return 0;
11696}
11697
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011698/*
11699 * Take commands from a file. To be compatible we should do a path
11700 * search for the file, which is necessary to find sub-commands.
11701 */
11702static char *
11703find_dot_file(char *name)
11704{
11705 char *fullname;
11706 const char *path = pathval();
11707 struct stat statb;
11708
11709 /* don't try this for absolute or relative paths */
11710 if (strchr(name, '/'))
11711 return name;
11712
11713 while ((fullname = padvance(&path, name)) != NULL) {
11714 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11715 /*
11716 * Don't bother freeing here, since it will
11717 * be freed by the caller.
11718 */
11719 return fullname;
11720 }
11721 stunalloc(fullname);
11722 }
11723
11724 /* not found in the PATH */
11725 ash_msg_and_raise_error("%s: not found", name);
11726 /* NOTREACHED */
11727}
11728
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011729static int
11730dotcmd(int argc, char **argv)
11731{
11732 struct strlist *sp;
11733 volatile struct shparam saveparam;
11734 int status = 0;
11735
11736 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011737 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011738
Denis Vlasenko68404f12008-03-17 09:00:54 +000011739 if (argv[1]) { /* That's what SVR2 does */
11740 char *fullname = find_dot_file(argv[1]);
11741 argv += 2;
11742 argc -= 2;
11743 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011744 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011745 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011746 shellparam.nparam = argc;
11747 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011748 };
11749
11750 setinputfile(fullname, INPUT_PUSH_FILE);
11751 commandname = fullname;
11752 cmdloop(0);
11753 popfile();
11754
Denis Vlasenko68404f12008-03-17 09:00:54 +000011755 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011756 freeparam(&shellparam);
11757 shellparam = saveparam;
11758 };
11759 status = exitstatus;
11760 }
11761 return status;
11762}
11763
11764static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011765exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011766{
11767 if (stoppedjobs())
11768 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011769 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011770 exitstatus = number(argv[1]);
11771 raise_exception(EXEXIT);
11772 /* NOTREACHED */
11773}
11774
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011775/*
11776 * Read a file containing shell functions.
11777 */
11778static void
11779readcmdfile(char *name)
11780{
11781 setinputfile(name, INPUT_PUSH_FILE);
11782 cmdloop(0);
11783 popfile();
11784}
11785
11786
Denis Vlasenkocc571512007-02-23 21:10:35 +000011787/* ============ find_command inplementation */
11788
11789/*
11790 * Resolve a command name. If you change this routine, you may have to
11791 * change the shellexec routine as well.
11792 */
11793static void
11794find_command(char *name, struct cmdentry *entry, int act, const char *path)
11795{
11796 struct tblentry *cmdp;
11797 int idx;
11798 int prev;
11799 char *fullname;
11800 struct stat statb;
11801 int e;
11802 int updatetbl;
11803 struct builtincmd *bcmd;
11804
11805 /* If name contains a slash, don't use PATH or hash table */
11806 if (strchr(name, '/') != NULL) {
11807 entry->u.index = -1;
11808 if (act & DO_ABS) {
11809 while (stat(name, &statb) < 0) {
11810#ifdef SYSV
11811 if (errno == EINTR)
11812 continue;
11813#endif
11814 entry->cmdtype = CMDUNKNOWN;
11815 return;
11816 }
11817 }
11818 entry->cmdtype = CMDNORMAL;
11819 return;
11820 }
11821
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011822/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011823
11824 updatetbl = (path == pathval());
11825 if (!updatetbl) {
11826 act |= DO_ALTPATH;
11827 if (strstr(path, "%builtin") != NULL)
11828 act |= DO_ALTBLTIN;
11829 }
11830
11831 /* If name is in the table, check answer will be ok */
11832 cmdp = cmdlookup(name, 0);
11833 if (cmdp != NULL) {
11834 int bit;
11835
11836 switch (cmdp->cmdtype) {
11837 default:
11838#if DEBUG
11839 abort();
11840#endif
11841 case CMDNORMAL:
11842 bit = DO_ALTPATH;
11843 break;
11844 case CMDFUNCTION:
11845 bit = DO_NOFUNC;
11846 break;
11847 case CMDBUILTIN:
11848 bit = DO_ALTBLTIN;
11849 break;
11850 }
11851 if (act & bit) {
11852 updatetbl = 0;
11853 cmdp = NULL;
11854 } else if (cmdp->rehash == 0)
11855 /* if not invalidated by cd, we're done */
11856 goto success;
11857 }
11858
11859 /* If %builtin not in path, check for builtin next */
11860 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011861 if (bcmd) {
11862 if (IS_BUILTIN_REGULAR(bcmd))
11863 goto builtin_success;
11864 if (act & DO_ALTPATH) {
11865 if (!(act & DO_ALTBLTIN))
11866 goto builtin_success;
11867 } else if (builtinloc <= 0) {
11868 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011869 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011870 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011871
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011872#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011873 {
11874 int applet_no = find_applet_by_name(name);
11875 if (applet_no >= 0) {
11876 entry->cmdtype = CMDNORMAL;
11877 entry->u.index = -2 - applet_no;
11878 return;
11879 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011880 }
11881#endif
11882
Denis Vlasenkocc571512007-02-23 21:10:35 +000011883 /* We have to search path. */
11884 prev = -1; /* where to start */
11885 if (cmdp && cmdp->rehash) { /* doing a rehash */
11886 if (cmdp->cmdtype == CMDBUILTIN)
11887 prev = builtinloc;
11888 else
11889 prev = cmdp->param.index;
11890 }
11891
11892 e = ENOENT;
11893 idx = -1;
11894 loop:
11895 while ((fullname = padvance(&path, name)) != NULL) {
11896 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011897 /* NB: code below will still use fullname
11898 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011899 idx++;
11900 if (pathopt) {
11901 if (prefix(pathopt, "builtin")) {
11902 if (bcmd)
11903 goto builtin_success;
11904 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011905 }
11906 if ((act & DO_NOFUNC)
11907 || !prefix(pathopt, "func")
11908 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011909 continue;
11910 }
11911 }
11912 /* if rehash, don't redo absolute path names */
11913 if (fullname[0] == '/' && idx <= prev) {
11914 if (idx < prev)
11915 continue;
11916 TRACE(("searchexec \"%s\": no change\n", name));
11917 goto success;
11918 }
11919 while (stat(fullname, &statb) < 0) {
11920#ifdef SYSV
11921 if (errno == EINTR)
11922 continue;
11923#endif
11924 if (errno != ENOENT && errno != ENOTDIR)
11925 e = errno;
11926 goto loop;
11927 }
11928 e = EACCES; /* if we fail, this will be the error */
11929 if (!S_ISREG(statb.st_mode))
11930 continue;
11931 if (pathopt) { /* this is a %func directory */
11932 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011933 /* NB: stalloc will return space pointed by fullname
11934 * (because we don't have any intervening allocations
11935 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011936 readcmdfile(fullname);
11937 cmdp = cmdlookup(name, 0);
11938 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11939 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11940 stunalloc(fullname);
11941 goto success;
11942 }
11943 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11944 if (!updatetbl) {
11945 entry->cmdtype = CMDNORMAL;
11946 entry->u.index = idx;
11947 return;
11948 }
11949 INT_OFF;
11950 cmdp = cmdlookup(name, 1);
11951 cmdp->cmdtype = CMDNORMAL;
11952 cmdp->param.index = idx;
11953 INT_ON;
11954 goto success;
11955 }
11956
11957 /* We failed. If there was an entry for this command, delete it */
11958 if (cmdp && updatetbl)
11959 delete_cmd_entry();
11960 if (act & DO_ERR)
11961 ash_msg("%s: %s", name, errmsg(e, "not found"));
11962 entry->cmdtype = CMDUNKNOWN;
11963 return;
11964
11965 builtin_success:
11966 if (!updatetbl) {
11967 entry->cmdtype = CMDBUILTIN;
11968 entry->u.cmd = bcmd;
11969 return;
11970 }
11971 INT_OFF;
11972 cmdp = cmdlookup(name, 1);
11973 cmdp->cmdtype = CMDBUILTIN;
11974 cmdp->param.cmd = bcmd;
11975 INT_ON;
11976 success:
11977 cmdp->rehash = 0;
11978 entry->cmdtype = cmdp->cmdtype;
11979 entry->u = cmdp->param;
11980}
11981
11982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011983/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011984
Eric Andersencb57d552001-06-28 07:25:16 +000011985/*
Eric Andersencb57d552001-06-28 07:25:16 +000011986 * The trap builtin.
11987 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011988static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011989trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011990{
11991 char *action;
11992 char **ap;
11993 int signo;
11994
Eric Andersenc470f442003-07-28 09:56:35 +000011995 nextopt(nullstr);
11996 ap = argptr;
11997 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011998 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011999 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012000 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012001 single_quote(trap[signo]),
12002 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012003 }
12004 }
12005 return 0;
12006 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012007 action = NULL;
12008 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012009 action = *ap++;
12010 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012011 signo = get_signum(*ap);
12012 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012013 ash_msg_and_raise_error("%s: bad trap", *ap);
12014 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012015 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012016 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012017 action = NULL;
12018 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012019 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012020 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012021 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012022 trap[signo] = action;
12023 if (signo != 0)
12024 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012025 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012026 ap++;
12027 }
12028 return 0;
12029}
12030
Eric Andersenc470f442003-07-28 09:56:35 +000012031
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012032/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012033
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012034#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012035/*
12036 * Lists available builtins
12037 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012038static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012039helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012040{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012041 unsigned col;
12042 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012043
12044 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012045 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012046 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012047 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012048 if (col > 60) {
12049 out1fmt("\n");
12050 col = 0;
12051 }
12052 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012053#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012054 {
12055 const char *a = applet_names;
12056 while (*a) {
12057 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12058 if (col > 60) {
12059 out1fmt("\n");
12060 col = 0;
12061 }
12062 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012063 }
12064 }
12065#endif
12066 out1fmt("\n\n");
12067 return EXIT_SUCCESS;
12068}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012069#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012070
Eric Andersencb57d552001-06-28 07:25:16 +000012071/*
Eric Andersencb57d552001-06-28 07:25:16 +000012072 * The export and readonly commands.
12073 */
Eric Andersenc470f442003-07-28 09:56:35 +000012074static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012075exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012076{
12077 struct var *vp;
12078 char *name;
12079 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012080 char **aptr;
12081 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012082
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012083 if (nextopt("p") != 'p') {
12084 aptr = argptr;
12085 name = *aptr;
12086 if (name) {
12087 do {
12088 p = strchr(name, '=');
12089 if (p != NULL) {
12090 p++;
12091 } else {
12092 vp = *findvar(hashvar(name), name);
12093 if (vp) {
12094 vp->flags |= flag;
12095 continue;
12096 }
Eric Andersencb57d552001-06-28 07:25:16 +000012097 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012098 setvar(name, p, flag);
12099 } while ((name = *++aptr) != NULL);
12100 return 0;
12101 }
Eric Andersencb57d552001-06-28 07:25:16 +000012102 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012103 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012104 return 0;
12105}
12106
Eric Andersencb57d552001-06-28 07:25:16 +000012107/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012108 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012109 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012110static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012111unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012112{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012113 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012114
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012115 cmdp = cmdlookup(name, 0);
12116 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12117 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012118}
12119
Eric Andersencb57d552001-06-28 07:25:16 +000012120/*
Eric Andersencb57d552001-06-28 07:25:16 +000012121 * The unset builtin command. We unset the function before we unset the
12122 * variable to allow a function to be unset when there is a readonly variable
12123 * with the same name.
12124 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012125static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012126unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012127{
12128 char **ap;
12129 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012130 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012131 int ret = 0;
12132
12133 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012134 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012135 }
Eric Andersencb57d552001-06-28 07:25:16 +000012136
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012137 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012138 if (flag != 'f') {
12139 i = unsetvar(*ap);
12140 ret |= i;
12141 if (!(i & 2))
12142 continue;
12143 }
12144 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012145 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012146 }
Eric Andersenc470f442003-07-28 09:56:35 +000012147 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012148}
12149
12150
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012151/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012152
Eric Andersenc470f442003-07-28 09:56:35 +000012153#include <sys/times.h>
12154
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012155static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012156 ' ', offsetof(struct tms, tms_utime),
12157 '\n', offsetof(struct tms, tms_stime),
12158 ' ', offsetof(struct tms, tms_cutime),
12159 '\n', offsetof(struct tms, tms_cstime),
12160 0
12161};
Eric Andersencb57d552001-06-28 07:25:16 +000012162
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012163static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012164timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012165{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012166 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012167 const unsigned char *p;
12168 struct tms buf;
12169
12170 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012171 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012172
12173 p = timescmd_str;
12174 do {
12175 t = *(clock_t *)(((char *) &buf) + p[1]);
12176 s = t / clk_tck;
12177 out1fmt("%ldm%ld.%.3lds%c",
12178 s/60, s%60,
12179 ((t - s * clk_tck) * 1000) / clk_tck,
12180 p[0]);
12181 } while (*(p += 2));
12182
Eric Andersencb57d552001-06-28 07:25:16 +000012183 return 0;
12184}
12185
Denis Vlasenko131ae172007-02-18 13:00:19 +000012186#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012187static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012188dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012189{
Eric Andersened9ecf72004-06-22 08:29:45 +000012190 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012191 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012192
Denis Vlasenkob012b102007-02-19 22:43:01 +000012193 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012194 result = arith(s, &errcode);
12195 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012196 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012197 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012198 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012199 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012200 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012201 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012202 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012203 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012204 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012205
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012206 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012207}
Eric Andersenc470f442003-07-28 09:56:35 +000012208
Eric Andersenc470f442003-07-28 09:56:35 +000012209/*
Eric Andersen90898442003-08-06 11:20:52 +000012210 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12211 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12212 *
12213 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012214 */
12215static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012216letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012217{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012218 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012219
Denis Vlasenko68404f12008-03-17 09:00:54 +000012220 argv++;
12221 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012222 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012223 do {
12224 i = dash_arith(*argv);
12225 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012226
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012227 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012228}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012229#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012230
Eric Andersenc470f442003-07-28 09:56:35 +000012231
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012232/* ============ miscbltin.c
12233 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012234 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012235 */
12236
12237#undef rflag
12238
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012239#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012240typedef enum __rlimit_resource rlim_t;
12241#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012242
Eric Andersenc470f442003-07-28 09:56:35 +000012243/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012244 * The read builtin. Options:
12245 * -r Do not interpret '\' specially
12246 * -s Turn off echo (tty only)
12247 * -n NCHARS Read NCHARS max
12248 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12249 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12250 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012251 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012252 * TODO: bash also has:
12253 * -a ARRAY Read into array[0],[1],etc
12254 * -d DELIM End on DELIM char, not newline
12255 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012256 */
Eric Andersenc470f442003-07-28 09:56:35 +000012257static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012258readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012259{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012260 static const char *const arg_REPLY[] = { "REPLY", NULL };
12261
Eric Andersenc470f442003-07-28 09:56:35 +000012262 char **ap;
12263 int backslash;
12264 char c;
12265 int rflag;
12266 char *prompt;
12267 const char *ifs;
12268 char *p;
12269 int startword;
12270 int status;
12271 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012272 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012273#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012274 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012275 int silent = 0;
12276 struct termios tty, old_tty;
12277#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012278#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012279 unsigned end_ms = 0;
12280 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012281#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012282
12283 rflag = 0;
12284 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012285 while ((i = nextopt("p:u:r"
12286 USE_ASH_READ_TIMEOUT("t:")
12287 USE_ASH_READ_NCHARS("n:s")
12288 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012289 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012290 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012291 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012292 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012293#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012294 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012295 nchars = bb_strtou(optionarg, NULL, 10);
12296 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012297 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012298 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012299 break;
12300 case 's':
12301 silent = 1;
12302 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012303#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012304#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012305 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012306 timeout = bb_strtou(optionarg, NULL, 10);
12307 if (errno || timeout > UINT_MAX / 2048)
12308 ash_msg_and_raise_error("invalid timeout");
12309 timeout *= 1000;
12310#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012311 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012312 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012313 /* EINVAL means number is ok, but not terminated by NUL */
12314 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012315 char *p2;
12316 if (*++p) {
12317 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012318 ts.tv_usec = bb_strtou(p, &p2, 10);
12319 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012320 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012321 scale = p2 - p;
12322 /* normalize to usec */
12323 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012324 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012325 while (scale++ < 6)
12326 ts.tv_usec *= 10;
12327 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012328 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012329 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012330 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012331 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012332 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012333 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012334#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012335 break;
12336#endif
12337 case 'r':
12338 rflag = 1;
12339 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012340 case 'u':
12341 fd = bb_strtou(optionarg, NULL, 10);
12342 if (fd < 0 || errno)
12343 ash_msg_and_raise_error("invalid file descriptor");
12344 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012345 default:
12346 break;
12347 }
Eric Andersenc470f442003-07-28 09:56:35 +000012348 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012349 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012350 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012351 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012352 ap = argptr;
12353 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012354 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012355 ifs = bltinlookup("IFS");
12356 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012357 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012358#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012359 tcgetattr(fd, &tty);
12360 old_tty = tty;
12361 if (nchars || silent) {
12362 if (nchars) {
12363 tty.c_lflag &= ~ICANON;
12364 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012365 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012366 if (silent) {
12367 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12368 }
12369 /* if tcgetattr failed, tcsetattr will fail too.
12370 * Ignoring, it's harmless. */
12371 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012372 }
12373#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012374
Eric Andersenc470f442003-07-28 09:56:35 +000012375 status = 0;
12376 startword = 1;
12377 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012378#if ENABLE_ASH_READ_TIMEOUT
12379 if (timeout) /* NB: ensuring end_ms is nonzero */
12380 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12381#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012382 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012383 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012384#if ENABLE_ASH_READ_TIMEOUT
12385 if (end_ms) {
12386 struct pollfd pfd[1];
12387 pfd[0].fd = fd;
12388 pfd[0].events = POLLIN;
12389 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12390 if ((int)timeout <= 0 /* already late? */
12391 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12392 ) { /* timed out! */
12393#if ENABLE_ASH_READ_NCHARS
12394 tcsetattr(fd, TCSANOW, &old_tty);
12395#endif
12396 return 1;
12397 }
12398 }
12399#endif
12400 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012401 status = 1;
12402 break;
12403 }
12404 if (c == '\0')
12405 continue;
12406 if (backslash) {
12407 backslash = 0;
12408 if (c != '\n')
12409 goto put;
12410 continue;
12411 }
12412 if (!rflag && c == '\\') {
12413 backslash++;
12414 continue;
12415 }
12416 if (c == '\n')
12417 break;
12418 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12419 continue;
12420 }
12421 startword = 0;
12422 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12423 STACKSTRNUL(p);
12424 setvar(*ap, stackblock(), 0);
12425 ap++;
12426 startword = 1;
12427 STARTSTACKSTR(p);
12428 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012429 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012430 STPUTC(c, p);
12431 }
12432 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012433/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012434#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012435 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012436#else
12437 while (1);
12438#endif
12439
12440#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012441 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012442#endif
12443
Eric Andersenc470f442003-07-28 09:56:35 +000012444 STACKSTRNUL(p);
12445 /* Remove trailing blanks */
12446 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12447 *p = '\0';
12448 setvar(*ap, stackblock(), 0);
12449 while (*++ap != NULL)
12450 setvar(*ap, nullstr, 0);
12451 return status;
12452}
12453
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012454static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012455umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012456{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012457 static const char permuser[3] ALIGN1 = "ugo";
12458 static const char permmode[3] ALIGN1 = "rwx";
12459 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012460 S_IRUSR, S_IWUSR, S_IXUSR,
12461 S_IRGRP, S_IWGRP, S_IXGRP,
12462 S_IROTH, S_IWOTH, S_IXOTH
12463 };
12464
12465 char *ap;
12466 mode_t mask;
12467 int i;
12468 int symbolic_mode = 0;
12469
12470 while (nextopt("S") != '\0') {
12471 symbolic_mode = 1;
12472 }
12473
Denis Vlasenkob012b102007-02-19 22:43:01 +000012474 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012475 mask = umask(0);
12476 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012477 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012478
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012479 ap = *argptr;
12480 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012481 if (symbolic_mode) {
12482 char buf[18];
12483 char *p = buf;
12484
12485 for (i = 0; i < 3; i++) {
12486 int j;
12487
12488 *p++ = permuser[i];
12489 *p++ = '=';
12490 for (j = 0; j < 3; j++) {
12491 if ((mask & permmask[3 * i + j]) == 0) {
12492 *p++ = permmode[j];
12493 }
12494 }
12495 *p++ = ',';
12496 }
12497 *--p = 0;
12498 puts(buf);
12499 } else {
12500 out1fmt("%.4o\n", mask);
12501 }
12502 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012503 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012504 mask = 0;
12505 do {
12506 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012507 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012508 mask = (mask << 3) + (*ap - '0');
12509 } while (*++ap != '\0');
12510 umask(mask);
12511 } else {
12512 mask = ~mask & 0777;
12513 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012514 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012515 }
12516 umask(~mask & 0777);
12517 }
12518 }
12519 return 0;
12520}
12521
12522/*
12523 * ulimit builtin
12524 *
12525 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12526 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12527 * ash by J.T. Conklin.
12528 *
12529 * Public domain.
12530 */
12531
12532struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012533 uint8_t cmd; /* RLIMIT_xxx fit into it */
12534 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012535 char option;
12536};
12537
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012538static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012539#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012540 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012541#endif
12542#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012543 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012544#endif
12545#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012546 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012547#endif
12548#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012549 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012550#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012551#ifdef RLIMIT_CORE
12552 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012553#endif
12554#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012555 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012556#endif
12557#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012558 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012559#endif
12560#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012561 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012562#endif
12563#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012564 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012565#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012566#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012567 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012568#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012569#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012570 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012571#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012572};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012573static const char limits_name[] =
12574#ifdef RLIMIT_CPU
12575 "time(seconds)" "\0"
12576#endif
12577#ifdef RLIMIT_FSIZE
12578 "file(blocks)" "\0"
12579#endif
12580#ifdef RLIMIT_DATA
12581 "data(kb)" "\0"
12582#endif
12583#ifdef RLIMIT_STACK
12584 "stack(kb)" "\0"
12585#endif
12586#ifdef RLIMIT_CORE
12587 "coredump(blocks)" "\0"
12588#endif
12589#ifdef RLIMIT_RSS
12590 "memory(kb)" "\0"
12591#endif
12592#ifdef RLIMIT_MEMLOCK
12593 "locked memory(kb)" "\0"
12594#endif
12595#ifdef RLIMIT_NPROC
12596 "process" "\0"
12597#endif
12598#ifdef RLIMIT_NOFILE
12599 "nofiles" "\0"
12600#endif
12601#ifdef RLIMIT_AS
12602 "vmemory(kb)" "\0"
12603#endif
12604#ifdef RLIMIT_LOCKS
12605 "locks" "\0"
12606#endif
12607;
Eric Andersenc470f442003-07-28 09:56:35 +000012608
Glenn L McGrath76620622004-01-13 10:19:37 +000012609enum limtype { SOFT = 0x1, HARD = 0x2 };
12610
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012611static void
12612printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012613 const struct limits *l)
12614{
12615 rlim_t val;
12616
12617 val = limit->rlim_max;
12618 if (how & SOFT)
12619 val = limit->rlim_cur;
12620
12621 if (val == RLIM_INFINITY)
12622 out1fmt("unlimited\n");
12623 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012624 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012625 out1fmt("%lld\n", (long long) val);
12626 }
12627}
12628
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012629static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012630ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012631{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012632 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012633 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012634 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012635 const struct limits *l;
12636 int set, all = 0;
12637 int optc, what;
12638 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012639
12640 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012641 while ((optc = nextopt("HSa"
12642#ifdef RLIMIT_CPU
12643 "t"
12644#endif
12645#ifdef RLIMIT_FSIZE
12646 "f"
12647#endif
12648#ifdef RLIMIT_DATA
12649 "d"
12650#endif
12651#ifdef RLIMIT_STACK
12652 "s"
12653#endif
12654#ifdef RLIMIT_CORE
12655 "c"
12656#endif
12657#ifdef RLIMIT_RSS
12658 "m"
12659#endif
12660#ifdef RLIMIT_MEMLOCK
12661 "l"
12662#endif
12663#ifdef RLIMIT_NPROC
12664 "p"
12665#endif
12666#ifdef RLIMIT_NOFILE
12667 "n"
12668#endif
12669#ifdef RLIMIT_AS
12670 "v"
12671#endif
12672#ifdef RLIMIT_LOCKS
12673 "w"
12674#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012675 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012676 switch (optc) {
12677 case 'H':
12678 how = HARD;
12679 break;
12680 case 'S':
12681 how = SOFT;
12682 break;
12683 case 'a':
12684 all = 1;
12685 break;
12686 default:
12687 what = optc;
12688 }
12689
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012690 for (l = limits_tbl; l->option != what; l++)
12691 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012692
12693 set = *argptr ? 1 : 0;
12694 if (set) {
12695 char *p = *argptr;
12696
12697 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012698 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012699 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012700 val = RLIM_INFINITY;
12701 else {
12702 val = (rlim_t) 0;
12703
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012704 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012705 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012706 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012707 if (val < (rlim_t) 0)
12708 break;
12709 }
12710 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012711 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012712 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012713 }
12714 }
12715 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012716 const char *lname = limits_name;
12717 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012718 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012719 out1fmt("%-20s ", lname);
12720 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012721 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012722 }
12723 return 0;
12724 }
12725
12726 getrlimit(l->cmd, &limit);
12727 if (set) {
12728 if (how & HARD)
12729 limit.rlim_max = val;
12730 if (how & SOFT)
12731 limit.rlim_cur = val;
12732 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012733 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012734 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012735 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012736 }
12737 return 0;
12738}
12739
Eric Andersen90898442003-08-06 11:20:52 +000012740
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012741/* ============ Math support */
12742
Denis Vlasenko131ae172007-02-18 13:00:19 +000012743#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012744
12745/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12746
12747 Permission is hereby granted, free of charge, to any person obtaining
12748 a copy of this software and associated documentation files (the
12749 "Software"), to deal in the Software without restriction, including
12750 without limitation the rights to use, copy, modify, merge, publish,
12751 distribute, sublicense, and/or sell copies of the Software, and to
12752 permit persons to whom the Software is furnished to do so, subject to
12753 the following conditions:
12754
12755 The above copyright notice and this permission notice shall be
12756 included in all copies or substantial portions of the Software.
12757
12758 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12759 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12760 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12761 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12762 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12763 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12764 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12765*/
12766
12767/* This is my infix parser/evaluator. It is optimized for size, intended
12768 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012769 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012770 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012771 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012772 * be that which POSIX specifies for shells. */
12773
12774/* The code uses a simple two-stack algorithm. See
12775 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012776 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012777 * this is based (this code differs in that it applies operators immediately
12778 * to the stack instead of adding them to a queue to end up with an
12779 * expression). */
12780
12781/* To use the routine, call it with an expression string and error return
12782 * pointer */
12783
12784/*
12785 * Aug 24, 2001 Manuel Novoa III
12786 *
12787 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12788 *
12789 * 1) In arith_apply():
12790 * a) Cached values of *numptr and &(numptr[-1]).
12791 * b) Removed redundant test for zero denominator.
12792 *
12793 * 2) In arith():
12794 * a) Eliminated redundant code for processing operator tokens by moving
12795 * to a table-based implementation. Also folded handling of parens
12796 * into the table.
12797 * b) Combined all 3 loops which called arith_apply to reduce generated
12798 * code size at the cost of speed.
12799 *
12800 * 3) The following expressions were treated as valid by the original code:
12801 * 1() , 0! , 1 ( *3 ) .
12802 * These bugs have been fixed by internally enclosing the expression in
12803 * parens and then checking that all binary ops and right parens are
12804 * preceded by a valid expression (NUM_TOKEN).
12805 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012806 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012807 * ctype's isspace() if it is used by another busybox applet or if additional
12808 * whitespace chars should be considered. Look below the "#include"s for a
12809 * precompiler test.
12810 */
12811
12812/*
12813 * Aug 26, 2001 Manuel Novoa III
12814 *
12815 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12816 *
12817 * Merge in Aaron's comments previously posted to the busybox list,
12818 * modified slightly to take account of my changes to the code.
12819 *
12820 */
12821
12822/*
12823 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12824 *
12825 * - allow access to variable,
12826 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12827 * - realize assign syntax (VAR=expr, +=, *= etc)
12828 * - realize exponentiation (** operator)
12829 * - realize comma separated - expr, expr
12830 * - realise ++expr --expr expr++ expr--
12831 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012832 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012833 * - was restored loses XOR operator
12834 * - remove one goto label, added three ;-)
12835 * - protect $((num num)) as true zero expr (Manuel`s error)
12836 * - always use special isspace(), see comment from bash ;-)
12837 */
12838
Eric Andersen90898442003-08-06 11:20:52 +000012839#define arith_isspace(arithval) \
12840 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12841
Eric Andersen90898442003-08-06 11:20:52 +000012842typedef unsigned char operator;
12843
12844/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012845 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012846 * precedence. The ID portion is so that multiple operators can have the
12847 * same precedence, ensuring that the leftmost one is evaluated first.
12848 * Consider * and /. */
12849
12850#define tok_decl(prec,id) (((id)<<5)|(prec))
12851#define PREC(op) ((op) & 0x1F)
12852
12853#define TOK_LPAREN tok_decl(0,0)
12854
12855#define TOK_COMMA tok_decl(1,0)
12856
12857#define TOK_ASSIGN tok_decl(2,0)
12858#define TOK_AND_ASSIGN tok_decl(2,1)
12859#define TOK_OR_ASSIGN tok_decl(2,2)
12860#define TOK_XOR_ASSIGN tok_decl(2,3)
12861#define TOK_PLUS_ASSIGN tok_decl(2,4)
12862#define TOK_MINUS_ASSIGN tok_decl(2,5)
12863#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12864#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12865
12866#define TOK_MUL_ASSIGN tok_decl(3,0)
12867#define TOK_DIV_ASSIGN tok_decl(3,1)
12868#define TOK_REM_ASSIGN tok_decl(3,2)
12869
12870/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012871#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012872
12873/* conditional is right associativity too */
12874#define TOK_CONDITIONAL tok_decl(4,0)
12875#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12876
12877#define TOK_OR tok_decl(5,0)
12878
12879#define TOK_AND tok_decl(6,0)
12880
12881#define TOK_BOR tok_decl(7,0)
12882
12883#define TOK_BXOR tok_decl(8,0)
12884
12885#define TOK_BAND tok_decl(9,0)
12886
12887#define TOK_EQ tok_decl(10,0)
12888#define TOK_NE tok_decl(10,1)
12889
12890#define TOK_LT tok_decl(11,0)
12891#define TOK_GT tok_decl(11,1)
12892#define TOK_GE tok_decl(11,2)
12893#define TOK_LE tok_decl(11,3)
12894
12895#define TOK_LSHIFT tok_decl(12,0)
12896#define TOK_RSHIFT tok_decl(12,1)
12897
12898#define TOK_ADD tok_decl(13,0)
12899#define TOK_SUB tok_decl(13,1)
12900
12901#define TOK_MUL tok_decl(14,0)
12902#define TOK_DIV tok_decl(14,1)
12903#define TOK_REM tok_decl(14,2)
12904
12905/* exponent is right associativity */
12906#define TOK_EXPONENT tok_decl(15,1)
12907
12908/* For now unary operators. */
12909#define UNARYPREC 16
12910#define TOK_BNOT tok_decl(UNARYPREC,0)
12911#define TOK_NOT tok_decl(UNARYPREC,1)
12912
12913#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12914#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12915
12916#define PREC_PRE (UNARYPREC+2)
12917
12918#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12919#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12920
12921#define PREC_POST (UNARYPREC+3)
12922
12923#define TOK_POST_INC tok_decl(PREC_POST, 0)
12924#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12925
12926#define SPEC_PREC (UNARYPREC+4)
12927
12928#define TOK_NUM tok_decl(SPEC_PREC, 0)
12929#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12930
12931#define NUMPTR (*numstackptr)
12932
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012933static int
12934tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012935{
12936 operator prec = PREC(op);
12937
12938 convert_prec_is_assing(prec);
12939 return (prec == PREC(TOK_ASSIGN) ||
12940 prec == PREC_PRE || prec == PREC_POST);
12941}
12942
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012943static int
12944is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012945{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012946 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12947 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012948}
12949
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012950typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012951 arith_t val;
12952 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012953 char contidional_second_val_initialized;
12954 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012955 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012956} v_n_t;
12957
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012958typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012959 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012960 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012961} chk_var_recursive_looped_t;
12962
12963static chk_var_recursive_looped_t *prev_chk_var_recursive;
12964
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012965static int
12966arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012967{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012968 if (t->var) {
12969 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012970
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012971 if (p) {
12972 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012973
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012974 /* recursive try as expression */
12975 chk_var_recursive_looped_t *cur;
12976 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012977
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012978 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12979 if (strcmp(cur->var, t->var) == 0) {
12980 /* expression recursion loop detected */
12981 return -5;
12982 }
12983 }
12984 /* save current lookuped var name */
12985 cur = prev_chk_var_recursive;
12986 cur_save.var = t->var;
12987 cur_save.next = cur;
12988 prev_chk_var_recursive = &cur_save;
12989
12990 t->val = arith (p, &errcode);
12991 /* restore previous ptr after recursiving */
12992 prev_chk_var_recursive = cur;
12993 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012994 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012995 /* allow undefined var as 0 */
12996 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012997 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012998 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012999}
13000
13001/* "applying" a token means performing it on the top elements on the integer
13002 * stack. For a unary operator it will only change the top element, but a
13003 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013004static int
13005arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013006{
Eric Andersen90898442003-08-06 11:20:52 +000013007 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013008 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013009 int ret_arith_lookup_val;
13010
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013011 /* There is no operator that can work without arguments */
13012 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013013 numptr_m1 = NUMPTR - 1;
13014
13015 /* check operand is var with noninteger value */
13016 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013018 return ret_arith_lookup_val;
13019
13020 rez = numptr_m1->val;
13021 if (op == TOK_UMINUS)
13022 rez *= -1;
13023 else if (op == TOK_NOT)
13024 rez = !rez;
13025 else if (op == TOK_BNOT)
13026 rez = ~rez;
13027 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13028 rez++;
13029 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13030 rez--;
13031 else if (op != TOK_UPLUS) {
13032 /* Binary operators */
13033
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013034 /* check and binary operators need two arguments */
13035 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013036
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013037 /* ... and they pop one */
13038 --NUMPTR;
13039 numptr_val = rez;
13040 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013041 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013042 /* protect $((expr1 ? expr2)) without ": expr" */
13043 goto err;
13044 }
13045 rez = numptr_m1->contidional_second_val;
13046 } else if (numptr_m1->contidional_second_val_initialized) {
13047 /* protect $((expr1 : expr2)) without "expr ? " */
13048 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013049 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013050 numptr_m1 = NUMPTR - 1;
13051 if (op != TOK_ASSIGN) {
13052 /* check operand is var with noninteger value for not '=' */
13053 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13054 if (ret_arith_lookup_val)
13055 return ret_arith_lookup_val;
13056 }
13057 if (op == TOK_CONDITIONAL) {
13058 numptr_m1->contidional_second_val = rez;
13059 }
13060 rez = numptr_m1->val;
13061 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013062 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013063 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013064 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013065 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013066 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013067 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013068 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013069 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013070 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013071 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013072 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013073 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013074 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013075 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013076 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013077 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013078 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013079 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013080 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013081 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013082 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013083 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013084 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013085 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013086 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013087 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013088 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013089 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013090 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013091 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013092 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013093 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013094 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013095 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013096 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013097 /* protect $((expr : expr)) without "expr ? " */
13098 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013099 }
13100 numptr_m1->contidional_second_val_initialized = op;
13101 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013102 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013103 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013104 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013105 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013106 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013107 return -3; /* exponent less than 0 */
13108 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013109 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013110
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013111 if (numptr_val)
13112 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013113 c *= rez;
13114 rez = c;
13115 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013116 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013117 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013118 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013119 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013120 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013121 rez %= numptr_val;
13122 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013123 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013124 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013125
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013126 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013127 /* Hmm, 1=2 ? */
13128 goto err;
13129 }
13130 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013131#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013132 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013133#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013134 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013135#endif
Eric Andersen90898442003-08-06 11:20:52 +000013136 setvar(numptr_m1->var, buf, 0);
13137 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013138 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013139 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013140 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013141 rez++;
13142 }
13143 numptr_m1->val = rez;
13144 /* protect geting var value, is number now */
13145 numptr_m1->var = NULL;
13146 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013147 err:
13148 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013149}
13150
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013151/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013152static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013153 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13154 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13155 '<','<', 0, TOK_LSHIFT,
13156 '>','>', 0, TOK_RSHIFT,
13157 '|','|', 0, TOK_OR,
13158 '&','&', 0, TOK_AND,
13159 '!','=', 0, TOK_NE,
13160 '<','=', 0, TOK_LE,
13161 '>','=', 0, TOK_GE,
13162 '=','=', 0, TOK_EQ,
13163 '|','=', 0, TOK_OR_ASSIGN,
13164 '&','=', 0, TOK_AND_ASSIGN,
13165 '*','=', 0, TOK_MUL_ASSIGN,
13166 '/','=', 0, TOK_DIV_ASSIGN,
13167 '%','=', 0, TOK_REM_ASSIGN,
13168 '+','=', 0, TOK_PLUS_ASSIGN,
13169 '-','=', 0, TOK_MINUS_ASSIGN,
13170 '-','-', 0, TOK_POST_DEC,
13171 '^','=', 0, TOK_XOR_ASSIGN,
13172 '+','+', 0, TOK_POST_INC,
13173 '*','*', 0, TOK_EXPONENT,
13174 '!', 0, TOK_NOT,
13175 '<', 0, TOK_LT,
13176 '>', 0, TOK_GT,
13177 '=', 0, TOK_ASSIGN,
13178 '|', 0, TOK_BOR,
13179 '&', 0, TOK_BAND,
13180 '*', 0, TOK_MUL,
13181 '/', 0, TOK_DIV,
13182 '%', 0, TOK_REM,
13183 '+', 0, TOK_ADD,
13184 '-', 0, TOK_SUB,
13185 '^', 0, TOK_BXOR,
13186 /* uniq */
13187 '~', 0, TOK_BNOT,
13188 ',', 0, TOK_COMMA,
13189 '?', 0, TOK_CONDITIONAL,
13190 ':', 0, TOK_CONDITIONAL_SEP,
13191 ')', 0, TOK_RPAREN,
13192 '(', 0, TOK_LPAREN,
13193 0
13194};
13195/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013196#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013197
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013198static arith_t
13199arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013200{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013201 char arithval; /* Current character under analysis */
13202 operator lasttok, op;
13203 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013204 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013205 const char *p = endexpression;
13206 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013207 v_n_t *numstack, *numstackptr;
13208 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013209
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013210 /* Stack of integers */
13211 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13212 * in any given correct or incorrect expression is left as an exercise to
13213 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013214 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013215 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013216 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013217
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013218 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13219 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013220
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013221 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013222 arithval = *expr;
13223 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013224 if (p == endexpression) {
13225 /* Null expression. */
13226 return 0;
13227 }
13228
13229 /* This is only reached after all tokens have been extracted from the
13230 * input stream. If there are still tokens on the operator stack, they
13231 * are to be applied in order. At the end, there should be a final
13232 * result on the integer stack */
13233
13234 if (expr != endexpression + 1) {
13235 /* If we haven't done so already, */
13236 /* append a closing right paren */
13237 expr = endexpression;
13238 /* and let the loop process it. */
13239 continue;
13240 }
13241 /* At this point, we're done with the expression. */
13242 if (numstackptr != numstack+1) {
13243 /* ... but if there isn't, it's bad */
13244 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013245 *perrcode = -1;
13246 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013247 }
13248 if (numstack->var) {
13249 /* expression is $((var)) only, lookup now */
13250 errcode = arith_lookup_val(numstack);
13251 }
13252 ret:
13253 *perrcode = errcode;
13254 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013255 }
13256
Eric Andersen90898442003-08-06 11:20:52 +000013257 /* Continue processing the expression. */
13258 if (arith_isspace(arithval)) {
13259 /* Skip whitespace */
13260 goto prologue;
13261 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013262 p = endofname(expr);
13263 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013264 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013265
13266 numstackptr->var = alloca(var_name_size);
13267 safe_strncpy(numstackptr->var, expr, var_name_size);
13268 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013269 num:
Eric Andersen90898442003-08-06 11:20:52 +000013270 numstackptr->contidional_second_val_initialized = 0;
13271 numstackptr++;
13272 lasttok = TOK_NUM;
13273 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013274 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013275 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013276 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013277#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013278 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013279#else
13280 numstackptr->val = strtol(expr, (char **) &expr, 0);
13281#endif
Eric Andersen90898442003-08-06 11:20:52 +000013282 goto num;
13283 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013284 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013285 const char *o;
13286
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013287 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013288 /* strange operator not found */
13289 goto err;
13290 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013291 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013292 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013293 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013294 /* found */
13295 expr = o - 1;
13296 break;
13297 }
13298 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013299 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013300 p++;
13301 /* skip zero delim */
13302 p++;
13303 }
13304 op = p[1];
13305
13306 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013307 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13308 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013309
13310 /* Plus and minus are binary (not unary) _only_ if the last
13311 * token was as number, or a right paren (which pretends to be
13312 * a number, since it evaluates to one). Think about it.
13313 * It makes sense. */
13314 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013315 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013316 case TOK_ADD:
13317 op = TOK_UPLUS;
13318 break;
13319 case TOK_SUB:
13320 op = TOK_UMINUS;
13321 break;
13322 case TOK_POST_INC:
13323 op = TOK_PRE_INC;
13324 break;
13325 case TOK_POST_DEC:
13326 op = TOK_PRE_DEC;
13327 break;
Eric Andersen90898442003-08-06 11:20:52 +000013328 }
13329 }
13330 /* We don't want a unary operator to cause recursive descent on the
13331 * stack, because there can be many in a row and it could cause an
13332 * operator to be evaluated before its argument is pushed onto the
13333 * integer stack. */
13334 /* But for binary operators, "apply" everything on the operator
13335 * stack until we find an operator with a lesser priority than the
13336 * one we have just extracted. */
13337 /* Left paren is given the lowest priority so it will never be
13338 * "applied" in this way.
13339 * if associativity is right and priority eq, applied also skip
13340 */
13341 prec = PREC(op);
13342 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13343 /* not left paren or unary */
13344 if (lasttok != TOK_NUM) {
13345 /* binary op must be preceded by a num */
13346 goto err;
13347 }
13348 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013349 if (op == TOK_RPAREN) {
13350 /* The algorithm employed here is simple: while we don't
13351 * hit an open paren nor the bottom of the stack, pop
13352 * tokens and apply them */
13353 if (stackptr[-1] == TOK_LPAREN) {
13354 --stackptr;
13355 /* Any operator directly after a */
13356 lasttok = TOK_NUM;
13357 /* close paren should consider itself binary */
13358 goto prologue;
13359 }
13360 } else {
13361 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013362
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013363 convert_prec_is_assing(prec);
13364 convert_prec_is_assing(prev_prec);
13365 if (prev_prec < prec)
13366 break;
13367 /* check right assoc */
13368 if (prev_prec == prec && is_right_associativity(prec))
13369 break;
13370 }
13371 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13372 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013373 }
13374 if (op == TOK_RPAREN) {
13375 goto err;
13376 }
13377 }
13378
13379 /* Push this operator to the stack and remember it. */
13380 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013381 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013382 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013383 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013384}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013385#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013386
13387
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013388/* ============ main() and helpers */
13389
13390/*
13391 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013392 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013393static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013394static void
13395exitshell(void)
13396{
13397 struct jmploc loc;
13398 char *p;
13399 int status;
13400
13401 status = exitstatus;
13402 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13403 if (setjmp(loc.loc)) {
13404 if (exception == EXEXIT)
13405/* dash bug: it just does _exit(exitstatus) here
13406 * but we have to do setjobctl(0) first!
13407 * (bug is still not fixed in dash-0.5.3 - if you run dash
13408 * under Midnight Commander, on exit from dash MC is backgrounded) */
13409 status = exitstatus;
13410 goto out;
13411 }
13412 exception_handler = &loc;
13413 p = trap[0];
13414 if (p) {
13415 trap[0] = NULL;
13416 evalstring(p, 0);
13417 }
13418 flush_stdout_stderr();
13419 out:
13420 setjobctl(0);
13421 _exit(status);
13422 /* NOTREACHED */
13423}
13424
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013425static void
13426init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013427{
13428 /* from input.c: */
13429 basepf.nextc = basepf.buf = basebuf;
13430
13431 /* from trap.c: */
13432 signal(SIGCHLD, SIG_DFL);
13433
13434 /* from var.c: */
13435 {
13436 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013437 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013438 const char *p;
13439 struct stat st1, st2;
13440
13441 initvar();
13442 for (envp = environ; envp && *envp; envp++) {
13443 if (strchr(*envp, '=')) {
13444 setvareq(*envp, VEXPORT|VTEXTFIXED);
13445 }
13446 }
13447
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013448 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013449 setvar("PPID", ppid, 0);
13450
13451 p = lookupvar("PWD");
13452 if (p)
13453 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13454 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13455 p = '\0';
13456 setpwd(p, 0);
13457 }
13458}
13459
13460/*
13461 * Process the shell command line arguments.
13462 */
13463static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013464procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013465{
13466 int i;
13467 const char *xminusc;
13468 char **xargv;
13469
13470 xargv = argv;
13471 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013472 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013473 xargv++;
13474 for (i = 0; i < NOPTS; i++)
13475 optlist[i] = 2;
13476 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013477 if (options(1)) {
13478 /* it already printed err message */
13479 raise_exception(EXERROR);
13480 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013481 xargv = argptr;
13482 xminusc = minusc;
13483 if (*xargv == NULL) {
13484 if (xminusc)
13485 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13486 sflag = 1;
13487 }
13488 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13489 iflag = 1;
13490 if (mflag == 2)
13491 mflag = iflag;
13492 for (i = 0; i < NOPTS; i++)
13493 if (optlist[i] == 2)
13494 optlist[i] = 0;
13495#if DEBUG == 2
13496 debug = 1;
13497#endif
13498 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13499 if (xminusc) {
13500 minusc = *xargv++;
13501 if (*xargv)
13502 goto setarg0;
13503 } else if (!sflag) {
13504 setinputfile(*xargv, 0);
13505 setarg0:
13506 arg0 = *xargv++;
13507 commandname = arg0;
13508 }
13509
13510 shellparam.p = xargv;
13511#if ENABLE_ASH_GETOPTS
13512 shellparam.optind = 1;
13513 shellparam.optoff = -1;
13514#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013515 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013516 while (*xargv) {
13517 shellparam.nparam++;
13518 xargv++;
13519 }
13520 optschanged();
13521}
13522
13523/*
13524 * Read /etc/profile or .profile.
13525 */
13526static void
13527read_profile(const char *name)
13528{
13529 int skip;
13530
13531 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13532 return;
13533 skip = cmdloop(0);
13534 popfile();
13535 if (skip)
13536 exitshell();
13537}
13538
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013539/*
13540 * This routine is called when an error or an interrupt occurs in an
13541 * interactive shell and control is returned to the main command loop.
13542 */
13543static void
13544reset(void)
13545{
13546 /* from eval.c: */
13547 evalskip = 0;
13548 loopnest = 0;
13549 /* from input.c: */
13550 parselleft = parsenleft = 0; /* clear input buffer */
13551 popallfiles();
13552 /* from parser.c: */
13553 tokpushback = 0;
13554 checkkwd = 0;
13555 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013556 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013557}
13558
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013559#if PROFILE
13560static short profile_buf[16384];
13561extern int etext();
13562#endif
13563
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013564/*
13565 * Main routine. We initialize things, parse the arguments, execute
13566 * profiles if we're a login shell, and then call cmdloop to execute
13567 * commands. The setjmp call sets up the location to jump to when an
13568 * exception occurs. When an exception occurs the variable "state"
13569 * is used to figure out how far we had gotten.
13570 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013571int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013572int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013573{
13574 char *shinit;
13575 volatile int state;
13576 struct jmploc jmploc;
13577 struct stackmark smark;
13578
Denis Vlasenko01631112007-12-16 17:20:38 +000013579 /* Initialize global data */
13580 INIT_G_misc();
13581 INIT_G_memstack();
13582 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013583#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013584 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013585#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013586 INIT_G_cmdtable();
13587
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013588#if PROFILE
13589 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13590#endif
13591
13592#if ENABLE_FEATURE_EDITING
13593 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13594#endif
13595 state = 0;
13596 if (setjmp(jmploc.loc)) {
13597 int e;
13598 int s;
13599
13600 reset();
13601
13602 e = exception;
13603 if (e == EXERROR)
13604 exitstatus = 2;
13605 s = state;
13606 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13607 exitshell();
13608
13609 if (e == EXINT) {
13610 outcslow('\n', stderr);
13611 }
13612 popstackmark(&smark);
13613 FORCE_INT_ON; /* enable interrupts */
13614 if (s == 1)
13615 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013616 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013617 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013618 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013619 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013620 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013621 }
13622 exception_handler = &jmploc;
13623#if DEBUG
13624 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013625 trace_puts("Shell args: ");
13626 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013627#endif
13628 rootpid = getpid();
13629
13630#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013631 /* Can use monotonic_ns() for better randomness but for now it is
13632 * not used anywhere else in busybox... so avoid bloat */
13633 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013634#endif
13635 init();
13636 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013637 procargs(argv);
13638
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013639#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13640 if (iflag) {
13641 const char *hp = lookupvar("HISTFILE");
13642
13643 if (hp == NULL) {
13644 hp = lookupvar("HOME");
13645 if (hp != NULL) {
13646 char *defhp = concat_path_file(hp, ".ash_history");
13647 setvar("HISTFILE", defhp, 0);
13648 free(defhp);
13649 }
13650 }
13651 }
13652#endif
13653 if (argv[0] && argv[0][0] == '-')
13654 isloginsh = 1;
13655 if (isloginsh) {
13656 state = 1;
13657 read_profile("/etc/profile");
13658 state1:
13659 state = 2;
13660 read_profile(".profile");
13661 }
13662 state2:
13663 state = 3;
13664 if (
13665#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013666 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013667#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013668 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013669 ) {
13670 shinit = lookupvar("ENV");
13671 if (shinit != NULL && *shinit != '\0') {
13672 read_profile(shinit);
13673 }
13674 }
13675 state3:
13676 state = 4;
13677 if (minusc)
13678 evalstring(minusc, 0);
13679
13680 if (sflag || minusc == NULL) {
13681#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013682 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013683 const char *hp = lookupvar("HISTFILE");
13684
13685 if (hp != NULL)
13686 line_input_state->hist_file = hp;
13687 }
13688#endif
13689 state4: /* XXX ??? - why isn't this before the "if" statement */
13690 cmdloop(1);
13691 }
13692#if PROFILE
13693 monitor(0);
13694#endif
13695#ifdef GPROF
13696 {
13697 extern void _mcleanup(void);
13698 _mcleanup();
13699 }
13700#endif
13701 exitshell();
13702 /* NOTREACHED */
13703}
13704
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013705#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013706const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013707int main(int argc, char **argv)
13708{
13709 return ash_main(argc, argv);
13710}
13711#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013712
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013713
Eric Andersendf82f612001-06-28 07:46:40 +000013714/*-
13715 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013716 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013717 *
13718 * This code is derived from software contributed to Berkeley by
13719 * Kenneth Almquist.
13720 *
13721 * Redistribution and use in source and binary forms, with or without
13722 * modification, are permitted provided that the following conditions
13723 * are met:
13724 * 1. Redistributions of source code must retain the above copyright
13725 * notice, this list of conditions and the following disclaimer.
13726 * 2. Redistributions in binary form must reproduce the above copyright
13727 * notice, this list of conditions and the following disclaimer in the
13728 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013729 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013730 * may be used to endorse or promote products derived from this software
13731 * without specific prior written permission.
13732 *
13733 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13734 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13735 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13736 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13737 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13738 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13739 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13740 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13741 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13742 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13743 * SUCH DAMAGE.
13744 */