blob: 55a79a7a621a2f8c98b20cb431e448db5414bdfe [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 Vlasenko340299a2008-11-21 10:36:36 +0000539#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000540
541union node;
542
543struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000544 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000545 union node *assign;
546 union node *args;
547 union node *redirect;
548};
549
550struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000551 smallint type;
552 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000553 struct nodelist *cmdlist;
554};
555
556struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000557 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000558 union node *n;
559 union node *redirect;
560};
561
562struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000563 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000564 union node *ch1;
565 union node *ch2;
566};
567
568struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000569 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000570 union node *test;
571 union node *ifpart;
572 union node *elsepart;
573};
574
575struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000576 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000577 union node *args;
578 union node *body;
579 char *var;
580};
581
582struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000583 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000584 union node *expr;
585 union node *cases;
586};
587
588struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000589 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000590 union node *next;
591 union node *pattern;
592 union node *body;
593};
594
595struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000596 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000597 union node *next;
598 char *text;
599 struct nodelist *backquote;
600};
601
Denis Vlasenko559691a2008-10-05 18:39:31 +0000602/* nfile and ndup layout must match!
603 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
604 * that it is actually NTO2 (>&file), and change its type.
605 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000606struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000607 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000608 union node *next;
609 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000610 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000611 union node *fname;
612 char *expfname;
613};
614
615struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000616 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000617 union node *next;
618 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000619 int dupfd;
620 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000621 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000622};
623
624struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000625 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000626 union node *next;
627 int fd;
628 union node *doc;
629};
630
631struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000632 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000633 union node *com;
634};
635
636union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000637 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000638 struct ncmd ncmd;
639 struct npipe npipe;
640 struct nredir nredir;
641 struct nbinary nbinary;
642 struct nif nif;
643 struct nfor nfor;
644 struct ncase ncase;
645 struct nclist nclist;
646 struct narg narg;
647 struct nfile nfile;
648 struct ndup ndup;
649 struct nhere nhere;
650 struct nnot nnot;
651};
652
653struct nodelist {
654 struct nodelist *next;
655 union node *n;
656};
657
658struct funcnode {
659 int count;
660 union node n;
661};
662
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000663/*
664 * Free a parse tree.
665 */
666static void
667freefunc(struct funcnode *f)
668{
669 if (f && --f->count < 0)
670 free(f);
671}
672
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000673
674/* ============ Debugging output */
675
676#if DEBUG
677
678static FILE *tracefile;
679
680static void
681trace_printf(const char *fmt, ...)
682{
683 va_list va;
684
685 if (debug != 1)
686 return;
687 va_start(va, fmt);
688 vfprintf(tracefile, fmt, va);
689 va_end(va);
690}
691
692static void
693trace_vprintf(const char *fmt, va_list va)
694{
695 if (debug != 1)
696 return;
697 vfprintf(tracefile, fmt, va);
698}
699
700static void
701trace_puts(const char *s)
702{
703 if (debug != 1)
704 return;
705 fputs(s, tracefile);
706}
707
708static void
709trace_puts_quoted(char *s)
710{
711 char *p;
712 char c;
713
714 if (debug != 1)
715 return;
716 putc('"', tracefile);
717 for (p = s; *p; p++) {
718 switch (*p) {
719 case '\n': c = 'n'; goto backslash;
720 case '\t': c = 't'; goto backslash;
721 case '\r': c = 'r'; goto backslash;
722 case '"': c = '"'; goto backslash;
723 case '\\': c = '\\'; goto backslash;
724 case CTLESC: c = 'e'; goto backslash;
725 case CTLVAR: c = 'v'; goto backslash;
726 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
727 case CTLBACKQ: c = 'q'; goto backslash;
728 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
729 backslash:
730 putc('\\', tracefile);
731 putc(c, tracefile);
732 break;
733 default:
734 if (*p >= ' ' && *p <= '~')
735 putc(*p, tracefile);
736 else {
737 putc('\\', tracefile);
738 putc(*p >> 6 & 03, tracefile);
739 putc(*p >> 3 & 07, tracefile);
740 putc(*p & 07, tracefile);
741 }
742 break;
743 }
744 }
745 putc('"', tracefile);
746}
747
748static void
749trace_puts_args(char **ap)
750{
751 if (debug != 1)
752 return;
753 if (!*ap)
754 return;
755 while (1) {
756 trace_puts_quoted(*ap);
757 if (!*++ap) {
758 putc('\n', tracefile);
759 break;
760 }
761 putc(' ', tracefile);
762 }
763}
764
765static void
766opentrace(void)
767{
768 char s[100];
769#ifdef O_APPEND
770 int flags;
771#endif
772
773 if (debug != 1) {
774 if (tracefile)
775 fflush(tracefile);
776 /* leave open because libedit might be using it */
777 return;
778 }
779 strcpy(s, "./trace");
780 if (tracefile) {
781 if (!freopen(s, "a", tracefile)) {
782 fprintf(stderr, "Can't re-open %s\n", s);
783 debug = 0;
784 return;
785 }
786 } else {
787 tracefile = fopen(s, "a");
788 if (tracefile == NULL) {
789 fprintf(stderr, "Can't open %s\n", s);
790 debug = 0;
791 return;
792 }
793 }
794#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000795 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000796 if (flags >= 0)
797 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
798#endif
799 setlinebuf(tracefile);
800 fputs("\nTracing started.\n", tracefile);
801}
802
803static void
804indent(int amount, char *pfx, FILE *fp)
805{
806 int i;
807
808 for (i = 0; i < amount; i++) {
809 if (pfx && i == amount - 1)
810 fputs(pfx, fp);
811 putc('\t', fp);
812 }
813}
814
815/* little circular references here... */
816static void shtree(union node *n, int ind, char *pfx, FILE *fp);
817
818static void
819sharg(union node *arg, FILE *fp)
820{
821 char *p;
822 struct nodelist *bqlist;
823 int subtype;
824
825 if (arg->type != NARG) {
826 out1fmt("<node type %d>\n", arg->type);
827 abort();
828 }
829 bqlist = arg->narg.backquote;
830 for (p = arg->narg.text; *p; p++) {
831 switch (*p) {
832 case CTLESC:
833 putc(*++p, fp);
834 break;
835 case CTLVAR:
836 putc('$', fp);
837 putc('{', fp);
838 subtype = *++p;
839 if (subtype == VSLENGTH)
840 putc('#', fp);
841
842 while (*p != '=')
843 putc(*p++, fp);
844
845 if (subtype & VSNUL)
846 putc(':', fp);
847
848 switch (subtype & VSTYPE) {
849 case VSNORMAL:
850 putc('}', fp);
851 break;
852 case VSMINUS:
853 putc('-', fp);
854 break;
855 case VSPLUS:
856 putc('+', fp);
857 break;
858 case VSQUESTION:
859 putc('?', fp);
860 break;
861 case VSASSIGN:
862 putc('=', fp);
863 break;
864 case VSTRIMLEFT:
865 putc('#', fp);
866 break;
867 case VSTRIMLEFTMAX:
868 putc('#', fp);
869 putc('#', fp);
870 break;
871 case VSTRIMRIGHT:
872 putc('%', fp);
873 break;
874 case VSTRIMRIGHTMAX:
875 putc('%', fp);
876 putc('%', fp);
877 break;
878 case VSLENGTH:
879 break;
880 default:
881 out1fmt("<subtype %d>", subtype);
882 }
883 break;
884 case CTLENDVAR:
885 putc('}', fp);
886 break;
887 case CTLBACKQ:
888 case CTLBACKQ|CTLQUOTE:
889 putc('$', fp);
890 putc('(', fp);
891 shtree(bqlist->n, -1, NULL, fp);
892 putc(')', fp);
893 break;
894 default:
895 putc(*p, fp);
896 break;
897 }
898 }
899}
900
901static void
902shcmd(union node *cmd, FILE *fp)
903{
904 union node *np;
905 int first;
906 const char *s;
907 int dftfd;
908
909 first = 1;
910 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000911 if (!first)
912 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000913 sharg(np, fp);
914 first = 0;
915 }
916 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000917 if (!first)
918 putc(' ', fp);
919 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000920 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000921 case NTO: s = ">>"+1; dftfd = 1; break;
922 case NCLOBBER: s = ">|"; dftfd = 1; break;
923 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000924#if ENABLE_ASH_BASH_COMPAT
925 case NTO2:
926#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000927 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000928 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000929 case NFROMFD: s = "<&"; break;
930 case NFROMTO: s = "<>"; break;
931 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000932 }
933 if (np->nfile.fd != dftfd)
934 fprintf(fp, "%d", np->nfile.fd);
935 fputs(s, fp);
936 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
937 fprintf(fp, "%d", np->ndup.dupfd);
938 } else {
939 sharg(np->nfile.fname, fp);
940 }
941 first = 0;
942 }
943}
944
945static void
946shtree(union node *n, int ind, char *pfx, FILE *fp)
947{
948 struct nodelist *lp;
949 const char *s;
950
951 if (n == NULL)
952 return;
953
954 indent(ind, pfx, fp);
955 switch (n->type) {
956 case NSEMI:
957 s = "; ";
958 goto binop;
959 case NAND:
960 s = " && ";
961 goto binop;
962 case NOR:
963 s = " || ";
964 binop:
965 shtree(n->nbinary.ch1, ind, NULL, fp);
966 /* if (ind < 0) */
967 fputs(s, fp);
968 shtree(n->nbinary.ch2, ind, NULL, fp);
969 break;
970 case NCMD:
971 shcmd(n, fp);
972 if (ind >= 0)
973 putc('\n', fp);
974 break;
975 case NPIPE:
976 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
977 shcmd(lp->n, fp);
978 if (lp->next)
979 fputs(" | ", fp);
980 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000981 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000982 fputs(" &", fp);
983 if (ind >= 0)
984 putc('\n', fp);
985 break;
986 default:
987 fprintf(fp, "<node type %d>", n->type);
988 if (ind >= 0)
989 putc('\n', fp);
990 break;
991 }
992}
993
994static void
995showtree(union node *n)
996{
997 trace_puts("showtree called\n");
998 shtree(n, 1, NULL, stdout);
999}
1000
1001#define TRACE(param) trace_printf param
1002#define TRACEV(param) trace_vprintf param
1003
1004#else
1005
1006#define TRACE(param)
1007#define TRACEV(param)
1008
1009#endif /* DEBUG */
1010
1011
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001012/* ============ Parser data */
1013
1014/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001015 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1016 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001017struct strlist {
1018 struct strlist *next;
1019 char *text;
1020};
1021
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001022struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001023
Denis Vlasenkob012b102007-02-19 22:43:01 +00001024struct strpush {
1025 struct strpush *prev; /* preceding string on stack */
1026 char *prevstring;
1027 int prevnleft;
1028#if ENABLE_ASH_ALIAS
1029 struct alias *ap; /* if push was associated with an alias */
1030#endif
1031 char *string; /* remember the string since it may change */
1032};
1033
1034struct parsefile {
1035 struct parsefile *prev; /* preceding file on stack */
1036 int linno; /* current line */
1037 int fd; /* file descriptor (or -1 if string) */
1038 int nleft; /* number of chars left in this line */
1039 int lleft; /* number of chars left in this buffer */
1040 char *nextc; /* next char in buffer */
1041 char *buf; /* input buffer */
1042 struct strpush *strpush; /* for pushing strings at this level */
1043 struct strpush basestrpush; /* so pushing one is fast */
1044};
1045
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001046static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001047static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001048static int startlinno; /* line # where last token started */
1049static char *commandname; /* currently executing command */
1050static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001051static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001052
1053
1054/* ============ Message printing */
1055
1056static void
1057ash_vmsg(const char *msg, va_list ap)
1058{
1059 fprintf(stderr, "%s: ", arg0);
1060 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001061 if (strcmp(arg0, commandname))
1062 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001063 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001064 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001065 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001066 vfprintf(stderr, msg, ap);
1067 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001068}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001069
1070/*
1071 * Exverror is called to raise the error exception. If the second argument
1072 * is not NULL then error prints an error message using printf style
1073 * formatting. It then raises the error exception.
1074 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001075static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001076static void
1077ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001078{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001079#if DEBUG
1080 if (msg) {
1081 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1082 TRACEV((msg, ap));
1083 TRACE(("\") pid=%d\n", getpid()));
1084 } else
1085 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1086 if (msg)
1087#endif
1088 ash_vmsg(msg, ap);
1089
1090 flush_stdout_stderr();
1091 raise_exception(cond);
1092 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001093}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001094
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001095static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001096static void
1097ash_msg_and_raise_error(const char *msg, ...)
1098{
1099 va_list ap;
1100
1101 va_start(ap, msg);
1102 ash_vmsg_and_raise(EXERROR, msg, ap);
1103 /* NOTREACHED */
1104 va_end(ap);
1105}
1106
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001107static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001108static void
1109ash_msg_and_raise(int cond, const char *msg, ...)
1110{
1111 va_list ap;
1112
1113 va_start(ap, msg);
1114 ash_vmsg_and_raise(cond, msg, ap);
1115 /* NOTREACHED */
1116 va_end(ap);
1117}
1118
1119/*
1120 * error/warning routines for external builtins
1121 */
1122static void
1123ash_msg(const char *fmt, ...)
1124{
1125 va_list ap;
1126
1127 va_start(ap, fmt);
1128 ash_vmsg(fmt, ap);
1129 va_end(ap);
1130}
1131
1132/*
1133 * Return a string describing an error. The returned string may be a
1134 * pointer to a static buffer that will be overwritten on the next call.
1135 * Action describes the operation that got the error.
1136 */
1137static const char *
1138errmsg(int e, const char *em)
1139{
1140 if (e == ENOENT || e == ENOTDIR) {
1141 return em;
1142 }
1143 return strerror(e);
1144}
1145
1146
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001147/* ============ Memory allocation */
1148
1149/*
1150 * It appears that grabstackstr() will barf with such alignments
1151 * because stalloc() will return a string allocated in a new stackblock.
1152 */
1153#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1154enum {
1155 /* Most machines require the value returned from malloc to be aligned
1156 * in some way. The following macro will get this right
1157 * on many machines. */
1158 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1159 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001160 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001161};
1162
1163struct stack_block {
1164 struct stack_block *prev;
1165 char space[MINSIZE];
1166};
1167
1168struct stackmark {
1169 struct stack_block *stackp;
1170 char *stacknxt;
1171 size_t stacknleft;
1172 struct stackmark *marknext;
1173};
1174
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001175
Denis Vlasenko01631112007-12-16 17:20:38 +00001176struct globals_memstack {
1177 struct stack_block *g_stackp; // = &stackbase;
1178 struct stackmark *markp;
1179 char *g_stacknxt; // = stackbase.space;
1180 char *sstrend; // = stackbase.space + MINSIZE;
1181 size_t g_stacknleft; // = MINSIZE;
1182 int herefd; // = -1;
1183 struct stack_block stackbase;
1184};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001185extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1186#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001187#define g_stackp (G_memstack.g_stackp )
1188#define markp (G_memstack.markp )
1189#define g_stacknxt (G_memstack.g_stacknxt )
1190#define sstrend (G_memstack.sstrend )
1191#define g_stacknleft (G_memstack.g_stacknleft)
1192#define herefd (G_memstack.herefd )
1193#define stackbase (G_memstack.stackbase )
1194#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001195 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1196 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001197 g_stackp = &stackbase; \
1198 g_stacknxt = stackbase.space; \
1199 g_stacknleft = MINSIZE; \
1200 sstrend = stackbase.space + MINSIZE; \
1201 herefd = -1; \
1202} while (0)
1203
1204#define stackblock() ((void *)g_stacknxt)
1205#define stackblocksize() g_stacknleft
1206
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001207
1208static void *
1209ckrealloc(void * p, size_t nbytes)
1210{
1211 p = realloc(p, nbytes);
1212 if (!p)
1213 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1214 return p;
1215}
1216
1217static void *
1218ckmalloc(size_t nbytes)
1219{
1220 return ckrealloc(NULL, nbytes);
1221}
1222
Denis Vlasenko597906c2008-02-20 16:38:54 +00001223static void *
1224ckzalloc(size_t nbytes)
1225{
1226 return memset(ckmalloc(nbytes), 0, nbytes);
1227}
1228
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001229/*
1230 * Make a copy of a string in safe storage.
1231 */
1232static char *
1233ckstrdup(const char *s)
1234{
1235 char *p = strdup(s);
1236 if (!p)
1237 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1238 return p;
1239}
1240
1241/*
1242 * Parse trees for commands are allocated in lifo order, so we use a stack
1243 * to make this more efficient, and also to avoid all sorts of exception
1244 * handling code to handle interrupts in the middle of a parse.
1245 *
1246 * The size 504 was chosen because the Ultrix malloc handles that size
1247 * well.
1248 */
1249static void *
1250stalloc(size_t nbytes)
1251{
1252 char *p;
1253 size_t aligned;
1254
1255 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001256 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001257 size_t len;
1258 size_t blocksize;
1259 struct stack_block *sp;
1260
1261 blocksize = aligned;
1262 if (blocksize < MINSIZE)
1263 blocksize = MINSIZE;
1264 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1265 if (len < blocksize)
1266 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1267 INT_OFF;
1268 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001269 sp->prev = g_stackp;
1270 g_stacknxt = sp->space;
1271 g_stacknleft = blocksize;
1272 sstrend = g_stacknxt + blocksize;
1273 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001274 INT_ON;
1275 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001276 p = g_stacknxt;
1277 g_stacknxt += aligned;
1278 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001279 return p;
1280}
1281
Denis Vlasenko597906c2008-02-20 16:38:54 +00001282static void *
1283stzalloc(size_t nbytes)
1284{
1285 return memset(stalloc(nbytes), 0, nbytes);
1286}
1287
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001288static void
1289stunalloc(void *p)
1290{
1291#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001292 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001293 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001294 abort();
1295 }
1296#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001297 g_stacknleft += g_stacknxt - (char *)p;
1298 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001299}
1300
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001301/*
1302 * Like strdup but works with the ash stack.
1303 */
1304static char *
1305ststrdup(const char *p)
1306{
1307 size_t len = strlen(p) + 1;
1308 return memcpy(stalloc(len), p, len);
1309}
1310
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001311static void
1312setstackmark(struct stackmark *mark)
1313{
Denis Vlasenko01631112007-12-16 17:20:38 +00001314 mark->stackp = g_stackp;
1315 mark->stacknxt = g_stacknxt;
1316 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001317 mark->marknext = markp;
1318 markp = mark;
1319}
1320
1321static void
1322popstackmark(struct stackmark *mark)
1323{
1324 struct stack_block *sp;
1325
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001326 if (!mark->stackp)
1327 return;
1328
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001329 INT_OFF;
1330 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001331 while (g_stackp != mark->stackp) {
1332 sp = g_stackp;
1333 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001334 free(sp);
1335 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001336 g_stacknxt = mark->stacknxt;
1337 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001338 sstrend = mark->stacknxt + mark->stacknleft;
1339 INT_ON;
1340}
1341
1342/*
1343 * When the parser reads in a string, it wants to stick the string on the
1344 * stack and only adjust the stack pointer when it knows how big the
1345 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1346 * of space on top of the stack and stackblocklen returns the length of
1347 * this block. Growstackblock will grow this space by at least one byte,
1348 * possibly moving it (like realloc). Grabstackblock actually allocates the
1349 * part of the block that has been used.
1350 */
1351static void
1352growstackblock(void)
1353{
1354 size_t newlen;
1355
Denis Vlasenko01631112007-12-16 17:20:38 +00001356 newlen = g_stacknleft * 2;
1357 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001358 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1359 if (newlen < 128)
1360 newlen += 128;
1361
Denis Vlasenko01631112007-12-16 17:20:38 +00001362 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001363 struct stack_block *oldstackp;
1364 struct stackmark *xmark;
1365 struct stack_block *sp;
1366 struct stack_block *prevstackp;
1367 size_t grosslen;
1368
1369 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001370 oldstackp = g_stackp;
1371 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001372 prevstackp = sp->prev;
1373 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1374 sp = ckrealloc(sp, grosslen);
1375 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001376 g_stackp = sp;
1377 g_stacknxt = sp->space;
1378 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001379 sstrend = sp->space + newlen;
1380
1381 /*
1382 * Stack marks pointing to the start of the old block
1383 * must be relocated to point to the new block
1384 */
1385 xmark = markp;
1386 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001387 xmark->stackp = g_stackp;
1388 xmark->stacknxt = g_stacknxt;
1389 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001390 xmark = xmark->marknext;
1391 }
1392 INT_ON;
1393 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001394 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001395 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001396 char *p = stalloc(newlen);
1397
1398 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001399 g_stacknxt = memcpy(p, oldspace, oldlen);
1400 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001401 }
1402}
1403
1404static void
1405grabstackblock(size_t len)
1406{
1407 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001408 g_stacknxt += len;
1409 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001410}
1411
1412/*
1413 * The following routines are somewhat easier to use than the above.
1414 * The user declares a variable of type STACKSTR, which may be declared
1415 * to be a register. The macro STARTSTACKSTR initializes things. Then
1416 * the user uses the macro STPUTC to add characters to the string. In
1417 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1418 * grown as necessary. When the user is done, she can just leave the
1419 * string there and refer to it using stackblock(). Or she can allocate
1420 * the space for it using grabstackstr(). If it is necessary to allow
1421 * someone else to use the stack temporarily and then continue to grow
1422 * the string, the user should use grabstack to allocate the space, and
1423 * then call ungrabstr(p) to return to the previous mode of operation.
1424 *
1425 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1426 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1427 * is space for at least one character.
1428 */
1429static void *
1430growstackstr(void)
1431{
1432 size_t len = stackblocksize();
1433 if (herefd >= 0 && len >= 1024) {
1434 full_write(herefd, stackblock(), len);
1435 return stackblock();
1436 }
1437 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001438 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001439}
1440
1441/*
1442 * Called from CHECKSTRSPACE.
1443 */
1444static char *
1445makestrspace(size_t newlen, char *p)
1446{
Denis Vlasenko01631112007-12-16 17:20:38 +00001447 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001448 size_t size = stackblocksize();
1449
1450 for (;;) {
1451 size_t nleft;
1452
1453 size = stackblocksize();
1454 nleft = size - len;
1455 if (nleft >= newlen)
1456 break;
1457 growstackblock();
1458 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001459 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001460}
1461
1462static char *
1463stack_nputstr(const char *s, size_t n, char *p)
1464{
1465 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001466 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001467 return p;
1468}
1469
1470static char *
1471stack_putstr(const char *s, char *p)
1472{
1473 return stack_nputstr(s, strlen(s), p);
1474}
1475
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001476static char *
1477_STPUTC(int c, char *p)
1478{
1479 if (p == sstrend)
1480 p = growstackstr();
1481 *p++ = c;
1482 return p;
1483}
1484
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001485#define STARTSTACKSTR(p) ((p) = stackblock())
1486#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001487#define CHECKSTRSPACE(n, p) do { \
1488 char *q = (p); \
1489 size_t l = (n); \
1490 size_t m = sstrend - q; \
1491 if (l > m) \
1492 (p) = makestrspace(l, q); \
1493} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001494#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001495#define STACKSTRNUL(p) do { \
1496 if ((p) == sstrend) \
1497 (p) = growstackstr(); \
1498 *(p) = '\0'; \
1499} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001500#define STUNPUTC(p) (--(p))
1501#define STTOPC(p) ((p)[-1])
1502#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001503
1504#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001505#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001506#define stackstrend() ((void *)sstrend)
1507
1508
1509/* ============ String helpers */
1510
1511/*
1512 * prefix -- see if pfx is a prefix of string.
1513 */
1514static char *
1515prefix(const char *string, const char *pfx)
1516{
1517 while (*pfx) {
1518 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001519 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001520 }
1521 return (char *) string;
1522}
1523
1524/*
1525 * Check for a valid number. This should be elsewhere.
1526 */
1527static int
1528is_number(const char *p)
1529{
1530 do {
1531 if (!isdigit(*p))
1532 return 0;
1533 } while (*++p != '\0');
1534 return 1;
1535}
1536
1537/*
1538 * Convert a string of digits to an integer, printing an error message on
1539 * failure.
1540 */
1541static int
1542number(const char *s)
1543{
1544 if (!is_number(s))
1545 ash_msg_and_raise_error(illnum, s);
1546 return atoi(s);
1547}
1548
1549/*
1550 * Produce a possibly single quoted string suitable as input to the shell.
1551 * The return string is allocated on the stack.
1552 */
1553static char *
1554single_quote(const char *s)
1555{
1556 char *p;
1557
1558 STARTSTACKSTR(p);
1559
1560 do {
1561 char *q;
1562 size_t len;
1563
1564 len = strchrnul(s, '\'') - s;
1565
1566 q = p = makestrspace(len + 3, p);
1567
1568 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001569 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001570 *q++ = '\'';
1571 s += len;
1572
1573 STADJUST(q - p, p);
1574
1575 len = strspn(s, "'");
1576 if (!len)
1577 break;
1578
1579 q = p = makestrspace(len + 3, p);
1580
1581 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001582 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001583 *q++ = '"';
1584 s += len;
1585
1586 STADJUST(q - p, p);
1587 } while (*s);
1588
1589 USTPUTC(0, p);
1590
1591 return stackblock();
1592}
1593
1594
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001595/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001596
1597static char **argptr; /* argument list for builtin commands */
1598static char *optionarg; /* set by nextopt (like getopt) */
1599static char *optptr; /* used by nextopt */
1600
1601/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001602 * XXX - should get rid of. Have all builtins use getopt(3).
1603 * The library getopt must have the BSD extension static variable
1604 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001605 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001606 * Standard option processing (a la getopt) for builtin routines.
1607 * The only argument that is passed to nextopt is the option string;
1608 * the other arguments are unnecessary. It returns the character,
1609 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001610 */
1611static int
1612nextopt(const char *optstring)
1613{
1614 char *p;
1615 const char *q;
1616 char c;
1617
1618 p = optptr;
1619 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001620 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001621 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001622 if (p == NULL)
1623 return '\0';
1624 if (*p != '-')
1625 return '\0';
1626 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001627 return '\0';
1628 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001629 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001630 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001631 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001632 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001633 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001634 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001635 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001636 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001637 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001638 if (*++q == ':')
1639 q++;
1640 }
1641 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001642 if (*p == '\0') {
1643 p = *argptr++;
1644 if (p == NULL)
1645 ash_msg_and_raise_error("no arg for -%c option", c);
1646 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001647 optionarg = p;
1648 p = NULL;
1649 }
1650 optptr = p;
1651 return c;
1652}
1653
1654
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001655/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001656
Denis Vlasenko01631112007-12-16 17:20:38 +00001657/*
1658 * The parsefile structure pointed to by the global variable parsefile
1659 * contains information about the current file being read.
1660 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001661struct shparam {
1662 int nparam; /* # of positional parameters (without $0) */
1663#if ENABLE_ASH_GETOPTS
1664 int optind; /* next parameter to be processed by getopts */
1665 int optoff; /* used by getopts */
1666#endif
1667 unsigned char malloced; /* if parameter list dynamically allocated */
1668 char **p; /* parameter list */
1669};
1670
1671/*
1672 * Free the list of positional parameters.
1673 */
1674static void
1675freeparam(volatile struct shparam *param)
1676{
Denis Vlasenko01631112007-12-16 17:20:38 +00001677 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001678 char **ap, **ap1;
1679 ap = ap1 = param->p;
1680 while (*ap)
1681 free(*ap++);
1682 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001683 }
1684}
1685
1686#if ENABLE_ASH_GETOPTS
1687static void getoptsreset(const char *value);
1688#endif
1689
1690struct var {
1691 struct var *next; /* next entry in hash list */
1692 int flags; /* flags are defined above */
1693 const char *text; /* name=value */
1694 void (*func)(const char *); /* function to be called when */
1695 /* the variable gets set/unset */
1696};
1697
1698struct localvar {
1699 struct localvar *next; /* next local variable in list */
1700 struct var *vp; /* the variable that was made local */
1701 int flags; /* saved flags */
1702 const char *text; /* saved text */
1703};
1704
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001705/* flags */
1706#define VEXPORT 0x01 /* variable is exported */
1707#define VREADONLY 0x02 /* variable cannot be modified */
1708#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1709#define VTEXTFIXED 0x08 /* text is statically allocated */
1710#define VSTACK 0x10 /* text is allocated on the stack */
1711#define VUNSET 0x20 /* the variable is not set */
1712#define VNOFUNC 0x40 /* don't call the callback function */
1713#define VNOSET 0x80 /* do not set variable - just readonly test */
1714#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001715#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001716# define VDYNAMIC 0x200 /* dynamic variable */
1717#else
1718# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001719#endif
1720
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001721#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001722static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723#define defifs (defifsvar + 4)
1724#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001725static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726#endif
1727
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001728
Denis Vlasenko01631112007-12-16 17:20:38 +00001729/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001730#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001731static void
1732change_lc_all(const char *value)
1733{
1734 if (value && *value != '\0')
1735 setlocale(LC_ALL, value);
1736}
1737static void
1738change_lc_ctype(const char *value)
1739{
1740 if (value && *value != '\0')
1741 setlocale(LC_CTYPE, value);
1742}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001743#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#if ENABLE_ASH_MAIL
1745static void chkmail(void);
1746static void changemail(const char *);
1747#endif
1748static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749#if ENABLE_ASH_RANDOM_SUPPORT
1750static void change_random(const char *);
1751#endif
1752
Denis Vlasenko01631112007-12-16 17:20:38 +00001753static const struct {
1754 int flags;
1755 const char *text;
1756 void (*func)(const char *);
1757} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001759 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001761 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001763#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001764 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1765 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001766#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001767 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1768 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1769 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1770 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001771#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001772 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001773#endif
1774#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001775 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776#endif
1777#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001778 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1779 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780#endif
1781#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001782 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783#endif
1784};
1785
Denis Vlasenko0b769642008-07-24 07:54:57 +00001786struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001787
1788struct globals_var {
1789 struct shparam shellparam; /* $@ current positional parameters */
1790 struct redirtab *redirlist;
1791 int g_nullredirs;
1792 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1793 struct var *vartab[VTABSIZE];
1794 struct var varinit[ARRAY_SIZE(varinit_data)];
1795};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001796extern struct globals_var *const ash_ptr_to_globals_var;
1797#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001798#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001799//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001800#define g_nullredirs (G_var.g_nullredirs )
1801#define preverrout_fd (G_var.preverrout_fd)
1802#define vartab (G_var.vartab )
1803#define varinit (G_var.varinit )
1804#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001805 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001806 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1807 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001808 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1809 varinit[i].flags = varinit_data[i].flags; \
1810 varinit[i].text = varinit_data[i].text; \
1811 varinit[i].func = varinit_data[i].func; \
1812 } \
1813} while (0)
1814
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001815#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001816#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001817# define vmail (&vifs)[1]
1818# define vmpath (&vmail)[1]
1819# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001820#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001821# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001822#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001823#define vps1 (&vpath)[1]
1824#define vps2 (&vps1)[1]
1825#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001826#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001827# define voptind (&vps4)[1]
1828# if ENABLE_ASH_RANDOM_SUPPORT
1829# define vrandom (&voptind)[1]
1830# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001831#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001832# if ENABLE_ASH_RANDOM_SUPPORT
1833# define vrandom (&vps4)[1]
1834# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001835#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001836
1837/*
1838 * The following macros access the values of the above variables.
1839 * They have to skip over the name. They return the null string
1840 * for unset variables.
1841 */
1842#define ifsval() (vifs.text + 4)
1843#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001844#if ENABLE_ASH_MAIL
1845# define mailval() (vmail.text + 5)
1846# define mpathval() (vmpath.text + 9)
1847# define mpathset() ((vmpath.flags & VUNSET) == 0)
1848#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001849#define pathval() (vpath.text + 5)
1850#define ps1val() (vps1.text + 4)
1851#define ps2val() (vps2.text + 4)
1852#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001853#if ENABLE_ASH_GETOPTS
1854# define optindval() (voptind.text + 7)
1855#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001856
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001857
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001858#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1859#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1860
Denis Vlasenko01631112007-12-16 17:20:38 +00001861#if ENABLE_ASH_GETOPTS
1862static void
1863getoptsreset(const char *value)
1864{
1865 shellparam.optind = number(value);
1866 shellparam.optoff = -1;
1867}
1868#endif
1869
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001870/*
1871 * Return of a legal variable name (a letter or underscore followed by zero or
1872 * more letters, underscores, and digits).
1873 */
1874static char *
1875endofname(const char *name)
1876{
1877 char *p;
1878
1879 p = (char *) name;
1880 if (!is_name(*p))
1881 return p;
1882 while (*++p) {
1883 if (!is_in_name(*p))
1884 break;
1885 }
1886 return p;
1887}
1888
1889/*
1890 * Compares two strings up to the first = or '\0'. The first
1891 * string must be terminated by '='; the second may be terminated by
1892 * either '=' or '\0'.
1893 */
1894static int
1895varcmp(const char *p, const char *q)
1896{
1897 int c, d;
1898
1899 while ((c = *p) == (d = *q)) {
1900 if (!c || c == '=')
1901 goto out;
1902 p++;
1903 q++;
1904 }
1905 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001906 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001907 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001908 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001909 out:
1910 return c - d;
1911}
1912
1913static int
1914varequal(const char *a, const char *b)
1915{
1916 return !varcmp(a, b);
1917}
1918
1919/*
1920 * Find the appropriate entry in the hash table from the name.
1921 */
1922static struct var **
1923hashvar(const char *p)
1924{
1925 unsigned hashval;
1926
1927 hashval = ((unsigned char) *p) << 4;
1928 while (*p && *p != '=')
1929 hashval += (unsigned char) *p++;
1930 return &vartab[hashval % VTABSIZE];
1931}
1932
1933static int
1934vpcmp(const void *a, const void *b)
1935{
1936 return varcmp(*(const char **)a, *(const char **)b);
1937}
1938
1939/*
1940 * This routine initializes the builtin variables.
1941 */
1942static void
1943initvar(void)
1944{
1945 struct var *vp;
1946 struct var *end;
1947 struct var **vpp;
1948
1949 /*
1950 * PS1 depends on uid
1951 */
1952#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1953 vps1.text = "PS1=\\w \\$ ";
1954#else
1955 if (!geteuid())
1956 vps1.text = "PS1=# ";
1957#endif
1958 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001959 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001960 do {
1961 vpp = hashvar(vp->text);
1962 vp->next = *vpp;
1963 *vpp = vp;
1964 } while (++vp < end);
1965}
1966
1967static struct var **
1968findvar(struct var **vpp, const char *name)
1969{
1970 for (; *vpp; vpp = &(*vpp)->next) {
1971 if (varequal((*vpp)->text, name)) {
1972 break;
1973 }
1974 }
1975 return vpp;
1976}
1977
1978/*
1979 * Find the value of a variable. Returns NULL if not set.
1980 */
1981static char *
1982lookupvar(const char *name)
1983{
1984 struct var *v;
1985
1986 v = *findvar(hashvar(name), name);
1987 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001988#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001989 /*
1990 * Dynamic variables are implemented roughly the same way they are
1991 * in bash. Namely, they're "special" so long as they aren't unset.
1992 * As soon as they're unset, they're no longer dynamic, and dynamic
1993 * lookup will no longer happen at that point. -- PFM.
1994 */
1995 if ((v->flags & VDYNAMIC))
1996 (*v->func)(NULL);
1997#endif
1998 if (!(v->flags & VUNSET))
1999 return strchrnul(v->text, '=') + 1;
2000 }
2001 return NULL;
2002}
2003
2004/*
2005 * Search the environment of a builtin command.
2006 */
2007static char *
2008bltinlookup(const char *name)
2009{
2010 struct strlist *sp;
2011
2012 for (sp = cmdenviron; sp; sp = sp->next) {
2013 if (varequal(sp->text, name))
2014 return strchrnul(sp->text, '=') + 1;
2015 }
2016 return lookupvar(name);
2017}
2018
2019/*
2020 * Same as setvar except that the variable and value are passed in
2021 * the first argument as name=value. Since the first argument will
2022 * be actually stored in the table, it should not be a string that
2023 * will go away.
2024 * Called with interrupts off.
2025 */
2026static void
2027setvareq(char *s, int flags)
2028{
2029 struct var *vp, **vpp;
2030
2031 vpp = hashvar(s);
2032 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2033 vp = *findvar(vpp, s);
2034 if (vp) {
2035 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2036 const char *n;
2037
2038 if (flags & VNOSAVE)
2039 free(s);
2040 n = vp->text;
2041 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2042 }
2043
2044 if (flags & VNOSET)
2045 return;
2046
2047 if (vp->func && (flags & VNOFUNC) == 0)
2048 (*vp->func)(strchrnul(s, '=') + 1);
2049
2050 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2051 free((char*)vp->text);
2052
2053 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2054 } else {
2055 if (flags & VNOSET)
2056 return;
2057 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002058 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002059 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002060 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002061 *vpp = vp;
2062 }
2063 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2064 s = ckstrdup(s);
2065 vp->text = s;
2066 vp->flags = flags;
2067}
2068
2069/*
2070 * Set the value of a variable. The flags argument is ored with the
2071 * flags of the variable. If val is NULL, the variable is unset.
2072 */
2073static void
2074setvar(const char *name, const char *val, int flags)
2075{
2076 char *p, *q;
2077 size_t namelen;
2078 char *nameeq;
2079 size_t vallen;
2080
2081 q = endofname(name);
2082 p = strchrnul(q, '=');
2083 namelen = p - name;
2084 if (!namelen || p != q)
2085 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2086 vallen = 0;
2087 if (val == NULL) {
2088 flags |= VUNSET;
2089 } else {
2090 vallen = strlen(val);
2091 }
2092 INT_OFF;
2093 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002094 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002095 if (val) {
2096 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002097 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002098 }
2099 *p = '\0';
2100 setvareq(nameeq, flags | VNOSAVE);
2101 INT_ON;
2102}
2103
2104#if ENABLE_ASH_GETOPTS
2105/*
2106 * Safe version of setvar, returns 1 on success 0 on failure.
2107 */
2108static int
2109setvarsafe(const char *name, const char *val, int flags)
2110{
2111 int err;
2112 volatile int saveint;
2113 struct jmploc *volatile savehandler = exception_handler;
2114 struct jmploc jmploc;
2115
2116 SAVE_INT(saveint);
2117 if (setjmp(jmploc.loc))
2118 err = 1;
2119 else {
2120 exception_handler = &jmploc;
2121 setvar(name, val, flags);
2122 err = 0;
2123 }
2124 exception_handler = savehandler;
2125 RESTORE_INT(saveint);
2126 return err;
2127}
2128#endif
2129
2130/*
2131 * Unset the specified variable.
2132 */
2133static int
2134unsetvar(const char *s)
2135{
2136 struct var **vpp;
2137 struct var *vp;
2138 int retval;
2139
2140 vpp = findvar(hashvar(s), s);
2141 vp = *vpp;
2142 retval = 2;
2143 if (vp) {
2144 int flags = vp->flags;
2145
2146 retval = 1;
2147 if (flags & VREADONLY)
2148 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002149#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002150 vp->flags &= ~VDYNAMIC;
2151#endif
2152 if (flags & VUNSET)
2153 goto ok;
2154 if ((flags & VSTRFIXED) == 0) {
2155 INT_OFF;
2156 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2157 free((char*)vp->text);
2158 *vpp = vp->next;
2159 free(vp);
2160 INT_ON;
2161 } else {
2162 setvar(s, 0, 0);
2163 vp->flags &= ~VEXPORT;
2164 }
2165 ok:
2166 retval = 0;
2167 }
2168 out:
2169 return retval;
2170}
2171
2172/*
2173 * Process a linked list of variable assignments.
2174 */
2175static void
2176listsetvar(struct strlist *list_set_var, int flags)
2177{
2178 struct strlist *lp = list_set_var;
2179
2180 if (!lp)
2181 return;
2182 INT_OFF;
2183 do {
2184 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002185 lp = lp->next;
2186 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002187 INT_ON;
2188}
2189
2190/*
2191 * Generate a list of variables satisfying the given conditions.
2192 */
2193static char **
2194listvars(int on, int off, char ***end)
2195{
2196 struct var **vpp;
2197 struct var *vp;
2198 char **ep;
2199 int mask;
2200
2201 STARTSTACKSTR(ep);
2202 vpp = vartab;
2203 mask = on | off;
2204 do {
2205 for (vp = *vpp; vp; vp = vp->next) {
2206 if ((vp->flags & mask) == on) {
2207 if (ep == stackstrend())
2208 ep = growstackstr();
2209 *ep++ = (char *) vp->text;
2210 }
2211 }
2212 } while (++vpp < vartab + VTABSIZE);
2213 if (ep == stackstrend())
2214 ep = growstackstr();
2215 if (end)
2216 *end = ep;
2217 *ep++ = NULL;
2218 return grabstackstr(ep);
2219}
2220
2221
2222/* ============ Path search helper
2223 *
2224 * The variable path (passed by reference) should be set to the start
2225 * of the path before the first call; padvance will update
2226 * this value as it proceeds. Successive calls to padvance will return
2227 * the possible path expansions in sequence. If an option (indicated by
2228 * a percent sign) appears in the path entry then the global variable
2229 * pathopt will be set to point to it; otherwise pathopt will be set to
2230 * NULL.
2231 */
2232static const char *pathopt; /* set by padvance */
2233
2234static char *
2235padvance(const char **path, const char *name)
2236{
2237 const char *p;
2238 char *q;
2239 const char *start;
2240 size_t len;
2241
2242 if (*path == NULL)
2243 return NULL;
2244 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002245 for (p = start; *p && *p != ':' && *p != '%'; p++)
2246 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002247 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2248 while (stackblocksize() < len)
2249 growstackblock();
2250 q = stackblock();
2251 if (p != start) {
2252 memcpy(q, start, p - start);
2253 q += p - start;
2254 *q++ = '/';
2255 }
2256 strcpy(q, name);
2257 pathopt = NULL;
2258 if (*p == '%') {
2259 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002260 while (*p && *p != ':')
2261 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002262 }
2263 if (*p == ':')
2264 *path = p + 1;
2265 else
2266 *path = NULL;
2267 return stalloc(len);
2268}
2269
2270
2271/* ============ Prompt */
2272
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002273static smallint doprompt; /* if set, prompt the user */
2274static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002275
2276#if ENABLE_FEATURE_EDITING
2277static line_input_t *line_input_state;
2278static const char *cmdedit_prompt;
2279static void
2280putprompt(const char *s)
2281{
2282 if (ENABLE_ASH_EXPAND_PRMT) {
2283 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002284 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002285 return;
2286 }
2287 cmdedit_prompt = s;
2288}
2289#else
2290static void
2291putprompt(const char *s)
2292{
2293 out2str(s);
2294}
2295#endif
2296
2297#if ENABLE_ASH_EXPAND_PRMT
2298/* expandstr() needs parsing machinery, so it is far away ahead... */
2299static const char *expandstr(const char *ps);
2300#else
2301#define expandstr(s) s
2302#endif
2303
2304static void
2305setprompt(int whichprompt)
2306{
2307 const char *prompt;
2308#if ENABLE_ASH_EXPAND_PRMT
2309 struct stackmark smark;
2310#endif
2311
2312 needprompt = 0;
2313
2314 switch (whichprompt) {
2315 case 1:
2316 prompt = ps1val();
2317 break;
2318 case 2:
2319 prompt = ps2val();
2320 break;
2321 default: /* 0 */
2322 prompt = nullstr;
2323 }
2324#if ENABLE_ASH_EXPAND_PRMT
2325 setstackmark(&smark);
2326 stalloc(stackblocksize());
2327#endif
2328 putprompt(expandstr(prompt));
2329#if ENABLE_ASH_EXPAND_PRMT
2330 popstackmark(&smark);
2331#endif
2332}
2333
2334
2335/* ============ The cd and pwd commands */
2336
2337#define CD_PHYSICAL 1
2338#define CD_PRINT 2
2339
2340static int docd(const char *, int);
2341
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002342static int
2343cdopt(void)
2344{
2345 int flags = 0;
2346 int i, j;
2347
2348 j = 'L';
2349 while ((i = nextopt("LP"))) {
2350 if (i != j) {
2351 flags ^= CD_PHYSICAL;
2352 j = i;
2353 }
2354 }
2355
2356 return flags;
2357}
2358
2359/*
2360 * Update curdir (the name of the current directory) in response to a
2361 * cd command.
2362 */
2363static const char *
2364updatepwd(const char *dir)
2365{
2366 char *new;
2367 char *p;
2368 char *cdcomppath;
2369 const char *lim;
2370
2371 cdcomppath = ststrdup(dir);
2372 STARTSTACKSTR(new);
2373 if (*dir != '/') {
2374 if (curdir == nullstr)
2375 return 0;
2376 new = stack_putstr(curdir, new);
2377 }
2378 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002379 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002380 if (*dir != '/') {
2381 if (new[-1] != '/')
2382 USTPUTC('/', new);
2383 if (new > lim && *lim == '/')
2384 lim++;
2385 } else {
2386 USTPUTC('/', new);
2387 cdcomppath++;
2388 if (dir[1] == '/' && dir[2] != '/') {
2389 USTPUTC('/', new);
2390 cdcomppath++;
2391 lim++;
2392 }
2393 }
2394 p = strtok(cdcomppath, "/");
2395 while (p) {
2396 switch (*p) {
2397 case '.':
2398 if (p[1] == '.' && p[2] == '\0') {
2399 while (new > lim) {
2400 STUNPUTC(new);
2401 if (new[-1] == '/')
2402 break;
2403 }
2404 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002405 }
2406 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002407 break;
2408 /* fall through */
2409 default:
2410 new = stack_putstr(p, new);
2411 USTPUTC('/', new);
2412 }
2413 p = strtok(0, "/");
2414 }
2415 if (new > lim)
2416 STUNPUTC(new);
2417 *new = 0;
2418 return stackblock();
2419}
2420
2421/*
2422 * Find out what the current directory is. If we already know the current
2423 * directory, this routine returns immediately.
2424 */
2425static char *
2426getpwd(void)
2427{
Denis Vlasenko01631112007-12-16 17:20:38 +00002428 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002429 return dir ? dir : nullstr;
2430}
2431
2432static void
2433setpwd(const char *val, int setold)
2434{
2435 char *oldcur, *dir;
2436
2437 oldcur = dir = curdir;
2438
2439 if (setold) {
2440 setvar("OLDPWD", oldcur, VEXPORT);
2441 }
2442 INT_OFF;
2443 if (physdir != nullstr) {
2444 if (physdir != oldcur)
2445 free(physdir);
2446 physdir = nullstr;
2447 }
2448 if (oldcur == val || !val) {
2449 char *s = getpwd();
2450 physdir = s;
2451 if (!val)
2452 dir = s;
2453 } else
2454 dir = ckstrdup(val);
2455 if (oldcur != dir && oldcur != nullstr) {
2456 free(oldcur);
2457 }
2458 curdir = dir;
2459 INT_ON;
2460 setvar("PWD", dir, VEXPORT);
2461}
2462
2463static void hashcd(void);
2464
2465/*
2466 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2467 * know that the current directory has changed.
2468 */
2469static int
2470docd(const char *dest, int flags)
2471{
2472 const char *dir = 0;
2473 int err;
2474
2475 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2476
2477 INT_OFF;
2478 if (!(flags & CD_PHYSICAL)) {
2479 dir = updatepwd(dest);
2480 if (dir)
2481 dest = dir;
2482 }
2483 err = chdir(dest);
2484 if (err)
2485 goto out;
2486 setpwd(dir, 1);
2487 hashcd();
2488 out:
2489 INT_ON;
2490 return err;
2491}
2492
2493static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002494cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002495{
2496 const char *dest;
2497 const char *path;
2498 const char *p;
2499 char c;
2500 struct stat statb;
2501 int flags;
2502
2503 flags = cdopt();
2504 dest = *argptr;
2505 if (!dest)
2506 dest = bltinlookup(homestr);
2507 else if (LONE_DASH(dest)) {
2508 dest = bltinlookup("OLDPWD");
2509 flags |= CD_PRINT;
2510 }
2511 if (!dest)
2512 dest = nullstr;
2513 if (*dest == '/')
2514 goto step7;
2515 if (*dest == '.') {
2516 c = dest[1];
2517 dotdot:
2518 switch (c) {
2519 case '\0':
2520 case '/':
2521 goto step6;
2522 case '.':
2523 c = dest[2];
2524 if (c != '.')
2525 goto dotdot;
2526 }
2527 }
2528 if (!*dest)
2529 dest = ".";
2530 path = bltinlookup("CDPATH");
2531 if (!path) {
2532 step6:
2533 step7:
2534 p = dest;
2535 goto docd;
2536 }
2537 do {
2538 c = *path;
2539 p = padvance(&path, dest);
2540 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2541 if (c && c != ':')
2542 flags |= CD_PRINT;
2543 docd:
2544 if (!docd(p, flags))
2545 goto out;
2546 break;
2547 }
2548 } while (path);
2549 ash_msg_and_raise_error("can't cd to %s", dest);
2550 /* NOTREACHED */
2551 out:
2552 if (flags & CD_PRINT)
2553 out1fmt(snlfmt, curdir);
2554 return 0;
2555}
2556
2557static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002558pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002559{
2560 int flags;
2561 const char *dir = curdir;
2562
2563 flags = cdopt();
2564 if (flags) {
2565 if (physdir == nullstr)
2566 setpwd(dir, 0);
2567 dir = physdir;
2568 }
2569 out1fmt(snlfmt, dir);
2570 return 0;
2571}
2572
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002573
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002574/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002575
Denis Vlasenko834dee72008-10-07 09:18:30 +00002576
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002577#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002578/* buffer for top level input file */
2579#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002580
Eric Andersenc470f442003-07-28 09:56:35 +00002581/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002582#define CWORD 0 /* character is nothing special */
2583#define CNL 1 /* newline character */
2584#define CBACK 2 /* a backslash character */
2585#define CSQUOTE 3 /* single quote */
2586#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002587#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002588#define CBQUOTE 6 /* backwards single quote */
2589#define CVAR 7 /* a dollar sign */
2590#define CENDVAR 8 /* a '}' character */
2591#define CLP 9 /* a left paren in arithmetic */
2592#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002593#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002594#define CCTL 12 /* like CWORD, except it must be escaped */
2595#define CSPCL 13 /* these terminate a word */
2596#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002597
Denis Vlasenko131ae172007-02-18 13:00:19 +00002598#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002599#define SYNBASE 130
2600#define PEOF -130
2601#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002602#define PEOA_OR_PEOF PEOA
2603#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002604#define SYNBASE 129
2605#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002606#define PEOA_OR_PEOF PEOF
2607#endif
2608
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002609/* number syntax index */
2610#define BASESYNTAX 0 /* not in quotes */
2611#define DQSYNTAX 1 /* in double quotes */
2612#define SQSYNTAX 2 /* in single quotes */
2613#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002614#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002615
Denis Vlasenko131ae172007-02-18 13:00:19 +00002616#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002617#define USE_SIT_FUNCTION
2618#endif
2619
Denis Vlasenko131ae172007-02-18 13:00:19 +00002620#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002621static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002622#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002623 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002624#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002625 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2626 { CNL, CNL, CNL, CNL }, /* 2, \n */
2627 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2628 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2629 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2630 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2631 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2632 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2633 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2634 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2635 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002636#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002637 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2638 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2639 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002640#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002641};
Eric Andersenc470f442003-07-28 09:56:35 +00002642#else
2643static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002644#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002645 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002646#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002647 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2648 { CNL, CNL, CNL }, /* 2, \n */
2649 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2650 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2651 { CVAR, CVAR, CWORD }, /* 5, $ */
2652 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2653 { CSPCL, CWORD, CWORD }, /* 7, ( */
2654 { CSPCL, CWORD, CWORD }, /* 8, ) */
2655 { CBACK, CBACK, CCTL }, /* 9, \ */
2656 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2657 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002658#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002659 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2660 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2661 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002662#endif
2663};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002664#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002665
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002666#ifdef USE_SIT_FUNCTION
2667
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002668static int
2669SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002670{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002671 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002672#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002673 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002674 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2675 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2676 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2677 11, 3 /* "}~" */
2678 };
2679#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002680 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002681 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2682 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2683 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2684 10, 2 /* "}~" */
2685 };
2686#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002687 const char *s;
2688 int indx;
2689
Eric Andersenc470f442003-07-28 09:56:35 +00002690 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002691 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002692#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002693 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002694 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002695 else
2696#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002697
2698 if ((unsigned char)c >= (unsigned char)(CTLESC)
2699 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2700 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002701 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002702 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002703 s = strchrnul(spec_symbls, c);
2704 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002705 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002706 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002707 }
2708 return S_I_T[indx][syntax];
2709}
2710
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002711#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002712
Denis Vlasenko131ae172007-02-18 13:00:19 +00002713#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002714#define CSPCL_CIGN_CIGN_CIGN 0
2715#define CSPCL_CWORD_CWORD_CWORD 1
2716#define CNL_CNL_CNL_CNL 2
2717#define CWORD_CCTL_CCTL_CWORD 3
2718#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2719#define CVAR_CVAR_CWORD_CVAR 5
2720#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2721#define CSPCL_CWORD_CWORD_CLP 7
2722#define CSPCL_CWORD_CWORD_CRP 8
2723#define CBACK_CBACK_CCTL_CBACK 9
2724#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2725#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2726#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2727#define CWORD_CWORD_CWORD_CWORD 13
2728#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002729#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002730#define CSPCL_CWORD_CWORD_CWORD 0
2731#define CNL_CNL_CNL_CNL 1
2732#define CWORD_CCTL_CCTL_CWORD 2
2733#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2734#define CVAR_CVAR_CWORD_CVAR 4
2735#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2736#define CSPCL_CWORD_CWORD_CLP 6
2737#define CSPCL_CWORD_CWORD_CRP 7
2738#define CBACK_CBACK_CCTL_CBACK 8
2739#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2740#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2741#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2742#define CWORD_CWORD_CWORD_CWORD 12
2743#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002744#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002745
2746static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002747 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002748 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002749#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002750 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2751#endif
2752 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2754 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2755 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2756 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2757 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2758 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2759 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2760 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002761 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2890 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2891 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2912 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2913 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002914 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002915 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2917 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002919 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002920 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2921 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2922 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2923 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2925 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2926 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2927 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2928 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2939 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2940 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2941 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2942 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2943 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2944 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2972 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2973 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2974 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2977 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3004 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3005 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3006 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3007 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003008};
3009
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003010#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3011
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003012#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003013
Eric Andersen2870d962001-07-02 17:27:21 +00003014
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003015/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003016
Denis Vlasenko131ae172007-02-18 13:00:19 +00003017#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003018
3019#define ALIASINUSE 1
3020#define ALIASDEAD 2
3021
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003022struct alias {
3023 struct alias *next;
3024 char *name;
3025 char *val;
3026 int flag;
3027};
3028
Denis Vlasenko01631112007-12-16 17:20:38 +00003029
3030static struct alias **atab; // [ATABSIZE];
3031#define INIT_G_alias() do { \
3032 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3033} while (0)
3034
Eric Andersen2870d962001-07-02 17:27:21 +00003035
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003036static struct alias **
3037__lookupalias(const char *name) {
3038 unsigned int hashval;
3039 struct alias **app;
3040 const char *p;
3041 unsigned int ch;
3042
3043 p = name;
3044
3045 ch = (unsigned char)*p;
3046 hashval = ch << 4;
3047 while (ch) {
3048 hashval += ch;
3049 ch = (unsigned char)*++p;
3050 }
3051 app = &atab[hashval % ATABSIZE];
3052
3053 for (; *app; app = &(*app)->next) {
3054 if (strcmp(name, (*app)->name) == 0) {
3055 break;
3056 }
3057 }
3058
3059 return app;
3060}
3061
3062static struct alias *
3063lookupalias(const char *name, int check)
3064{
3065 struct alias *ap = *__lookupalias(name);
3066
3067 if (check && ap && (ap->flag & ALIASINUSE))
3068 return NULL;
3069 return ap;
3070}
3071
3072static struct alias *
3073freealias(struct alias *ap)
3074{
3075 struct alias *next;
3076
3077 if (ap->flag & ALIASINUSE) {
3078 ap->flag |= ALIASDEAD;
3079 return ap;
3080 }
3081
3082 next = ap->next;
3083 free(ap->name);
3084 free(ap->val);
3085 free(ap);
3086 return next;
3087}
Eric Andersencb57d552001-06-28 07:25:16 +00003088
Eric Andersenc470f442003-07-28 09:56:35 +00003089static void
3090setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003091{
3092 struct alias *ap, **app;
3093
3094 app = __lookupalias(name);
3095 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003096 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003097 if (ap) {
3098 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003099 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003100 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003101 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003102 ap->flag &= ~ALIASDEAD;
3103 } else {
3104 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003105 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003106 ap->name = ckstrdup(name);
3107 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003108 /*ap->flag = 0; - ckzalloc did it */
3109 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003110 *app = ap;
3111 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003112 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003113}
3114
Eric Andersenc470f442003-07-28 09:56:35 +00003115static int
3116unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003117{
Eric Andersencb57d552001-06-28 07:25:16 +00003118 struct alias **app;
3119
3120 app = __lookupalias(name);
3121
3122 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003123 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003124 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003125 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003126 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003127 }
3128
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003129 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003130}
3131
Eric Andersenc470f442003-07-28 09:56:35 +00003132static void
3133rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003134{
Eric Andersencb57d552001-06-28 07:25:16 +00003135 struct alias *ap, **app;
3136 int i;
3137
Denis Vlasenkob012b102007-02-19 22:43:01 +00003138 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003139 for (i = 0; i < ATABSIZE; i++) {
3140 app = &atab[i];
3141 for (ap = *app; ap; ap = *app) {
3142 *app = freealias(*app);
3143 if (ap == *app) {
3144 app = &ap->next;
3145 }
3146 }
3147 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003148 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003149}
3150
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003151static void
3152printalias(const struct alias *ap)
3153{
3154 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3155}
3156
Eric Andersencb57d552001-06-28 07:25:16 +00003157/*
3158 * TODO - sort output
3159 */
Eric Andersenc470f442003-07-28 09:56:35 +00003160static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003161aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003162{
3163 char *n, *v;
3164 int ret = 0;
3165 struct alias *ap;
3166
Denis Vlasenko68404f12008-03-17 09:00:54 +00003167 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003168 int i;
3169
Denis Vlasenko68404f12008-03-17 09:00:54 +00003170 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003171 for (ap = atab[i]; ap; ap = ap->next) {
3172 printalias(ap);
3173 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003174 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003175 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003176 }
3177 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003178 v = strchr(n+1, '=');
3179 if (v == NULL) { /* n+1: funny ksh stuff */
3180 ap = *__lookupalias(n);
3181 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003182 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003183 ret = 1;
3184 } else
3185 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003186 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003187 *v++ = '\0';
3188 setalias(n, v);
3189 }
3190 }
3191
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003192 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003193}
3194
Eric Andersenc470f442003-07-28 09:56:35 +00003195static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003196unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003197{
3198 int i;
3199
3200 while ((i = nextopt("a")) != '\0') {
3201 if (i == 'a') {
3202 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003203 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003204 }
3205 }
3206 for (i = 0; *argptr; argptr++) {
3207 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003208 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003209 i = 1;
3210 }
3211 }
3212
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003213 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003214}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003215
Denis Vlasenko131ae172007-02-18 13:00:19 +00003216#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003217
Eric Andersenc470f442003-07-28 09:56:35 +00003218
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003219/* ============ jobs.c */
3220
3221/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3222#define FORK_FG 0
3223#define FORK_BG 1
3224#define FORK_NOJOB 2
3225
3226/* mode flags for showjob(s) */
3227#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3228#define SHOW_PID 0x04 /* include process pid */
3229#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3230
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003231/*
3232 * A job structure contains information about a job. A job is either a
3233 * single process or a set of processes contained in a pipeline. In the
3234 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3235 * array of pids.
3236 */
3237
3238struct procstat {
3239 pid_t pid; /* process id */
3240 int status; /* last process status from wait() */
3241 char *cmd; /* text of command being run */
3242};
3243
3244struct job {
3245 struct procstat ps0; /* status of process */
3246 struct procstat *ps; /* status or processes when more than one */
3247#if JOBS
3248 int stopstatus; /* status of a stopped job */
3249#endif
3250 uint32_t
3251 nprocs: 16, /* number of processes */
3252 state: 8,
3253#define JOBRUNNING 0 /* at least one proc running */
3254#define JOBSTOPPED 1 /* all procs are stopped */
3255#define JOBDONE 2 /* all procs are completed */
3256#if JOBS
3257 sigint: 1, /* job was killed by SIGINT */
3258 jobctl: 1, /* job running under job control */
3259#endif
3260 waited: 1, /* true if this entry has been waited for */
3261 used: 1, /* true if this entry is in used */
3262 changed: 1; /* true if status has changed */
3263 struct job *prev_job; /* previous job */
3264};
3265
Denis Vlasenko68404f12008-03-17 09:00:54 +00003266static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003267#if !JOBS
3268#define forkshell(job, node, mode) forkshell(job, mode)
3269#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003270static int forkshell(struct job *, union node *, int);
3271static int waitforjob(struct job *);
3272
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003273#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003274enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003275#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003276#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003277static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003278static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003279#endif
3280
3281/*
3282 * Set the signal handler for the specified signal. The routine figures
3283 * out what it should be set to.
3284 */
3285static void
3286setsignal(int signo)
3287{
3288 int action;
3289 char *t, tsig;
3290 struct sigaction act;
3291
3292 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003293 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003294 if (t == NULL)
3295 action = S_DFL;
3296 else if (*t != '\0')
3297 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003298 if (rootshell && action == S_DFL) {
3299 switch (signo) {
3300 case SIGINT:
3301 if (iflag || minusc || sflag == 0)
3302 action = S_CATCH;
3303 break;
3304 case SIGQUIT:
3305#if DEBUG
3306 if (debug)
3307 break;
3308#endif
3309 /* FALLTHROUGH */
3310 case SIGTERM:
3311 if (iflag)
3312 action = S_IGN;
3313 break;
3314#if JOBS
3315 case SIGTSTP:
3316 case SIGTTOU:
3317 if (mflag)
3318 action = S_IGN;
3319 break;
3320#endif
3321 }
3322 }
3323
3324 t = &sigmode[signo - 1];
3325 tsig = *t;
3326 if (tsig == 0) {
3327 /*
3328 * current setting unknown
3329 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003330 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331 /*
3332 * Pretend it worked; maybe we should give a warning
3333 * here, but other shells don't. We don't alter
3334 * sigmode, so that we retry every time.
3335 */
3336 return;
3337 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003338 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003339 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003340 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003341 if (mflag
3342 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3343 ) {
3344 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003345 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003346 }
3347 }
3348 if (tsig == S_HARD_IGN || tsig == action)
3349 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003350 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003351 switch (action) {
3352 case S_CATCH:
3353 act.sa_handler = onsig;
3354 break;
3355 case S_IGN:
3356 act.sa_handler = SIG_IGN;
3357 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003358 }
3359 *t = action;
3360 act.sa_flags = 0;
3361 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003362 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003363}
3364
3365/* mode flags for set_curjob */
3366#define CUR_DELETE 2
3367#define CUR_RUNNING 1
3368#define CUR_STOPPED 0
3369
3370/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003371#define DOWAIT_NONBLOCK WNOHANG
3372#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373
3374#if JOBS
3375/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003376static int initialpgrp; //references:2
3377static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003378#endif
3379/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003380static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003381/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003382static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003383/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003384static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003385/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003386static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003387
3388static void
3389set_curjob(struct job *jp, unsigned mode)
3390{
3391 struct job *jp1;
3392 struct job **jpp, **curp;
3393
3394 /* first remove from list */
3395 jpp = curp = &curjob;
3396 do {
3397 jp1 = *jpp;
3398 if (jp1 == jp)
3399 break;
3400 jpp = &jp1->prev_job;
3401 } while (1);
3402 *jpp = jp1->prev_job;
3403
3404 /* Then re-insert in correct position */
3405 jpp = curp;
3406 switch (mode) {
3407 default:
3408#if DEBUG
3409 abort();
3410#endif
3411 case CUR_DELETE:
3412 /* job being deleted */
3413 break;
3414 case CUR_RUNNING:
3415 /* newly created job or backgrounded job,
3416 put after all stopped jobs. */
3417 do {
3418 jp1 = *jpp;
3419#if JOBS
3420 if (!jp1 || jp1->state != JOBSTOPPED)
3421#endif
3422 break;
3423 jpp = &jp1->prev_job;
3424 } while (1);
3425 /* FALLTHROUGH */
3426#if JOBS
3427 case CUR_STOPPED:
3428#endif
3429 /* newly stopped job - becomes curjob */
3430 jp->prev_job = *jpp;
3431 *jpp = jp;
3432 break;
3433 }
3434}
3435
3436#if JOBS || DEBUG
3437static int
3438jobno(const struct job *jp)
3439{
3440 return jp - jobtab + 1;
3441}
3442#endif
3443
3444/*
3445 * Convert a job name to a job structure.
3446 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003447#if !JOBS
3448#define getjob(name, getctl) getjob(name)
3449#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003450static struct job *
3451getjob(const char *name, int getctl)
3452{
3453 struct job *jp;
3454 struct job *found;
3455 const char *err_msg = "No such job: %s";
3456 unsigned num;
3457 int c;
3458 const char *p;
3459 char *(*match)(const char *, const char *);
3460
3461 jp = curjob;
3462 p = name;
3463 if (!p)
3464 goto currentjob;
3465
3466 if (*p != '%')
3467 goto err;
3468
3469 c = *++p;
3470 if (!c)
3471 goto currentjob;
3472
3473 if (!p[1]) {
3474 if (c == '+' || c == '%') {
3475 currentjob:
3476 err_msg = "No current job";
3477 goto check;
3478 }
3479 if (c == '-') {
3480 if (jp)
3481 jp = jp->prev_job;
3482 err_msg = "No previous job";
3483 check:
3484 if (!jp)
3485 goto err;
3486 goto gotit;
3487 }
3488 }
3489
3490 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003491// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003492 num = atoi(p);
3493 if (num < njobs) {
3494 jp = jobtab + num - 1;
3495 if (jp->used)
3496 goto gotit;
3497 goto err;
3498 }
3499 }
3500
3501 match = prefix;
3502 if (*p == '?') {
3503 match = strstr;
3504 p++;
3505 }
3506
3507 found = 0;
3508 while (1) {
3509 if (!jp)
3510 goto err;
3511 if (match(jp->ps[0].cmd, p)) {
3512 if (found)
3513 goto err;
3514 found = jp;
3515 err_msg = "%s: ambiguous";
3516 }
3517 jp = jp->prev_job;
3518 }
3519
3520 gotit:
3521#if JOBS
3522 err_msg = "job %s not created under job control";
3523 if (getctl && jp->jobctl == 0)
3524 goto err;
3525#endif
3526 return jp;
3527 err:
3528 ash_msg_and_raise_error(err_msg, name);
3529}
3530
3531/*
3532 * Mark a job structure as unused.
3533 */
3534static void
3535freejob(struct job *jp)
3536{
3537 struct procstat *ps;
3538 int i;
3539
3540 INT_OFF;
3541 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3542 if (ps->cmd != nullstr)
3543 free(ps->cmd);
3544 }
3545 if (jp->ps != &jp->ps0)
3546 free(jp->ps);
3547 jp->used = 0;
3548 set_curjob(jp, CUR_DELETE);
3549 INT_ON;
3550}
3551
3552#if JOBS
3553static void
3554xtcsetpgrp(int fd, pid_t pgrp)
3555{
3556 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003557 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003558}
3559
3560/*
3561 * Turn job control on and off.
3562 *
3563 * Note: This code assumes that the third arg to ioctl is a character
3564 * pointer, which is true on Berkeley systems but not System V. Since
3565 * System V doesn't have job control yet, this isn't a problem now.
3566 *
3567 * Called with interrupts off.
3568 */
3569static void
3570setjobctl(int on)
3571{
3572 int fd;
3573 int pgrp;
3574
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003575 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003576 return;
3577 if (on) {
3578 int ofd;
3579 ofd = fd = open(_PATH_TTY, O_RDWR);
3580 if (fd < 0) {
3581 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3582 * That sometimes helps to acquire controlling tty.
3583 * Obviously, a workaround for bugs when someone
3584 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003585 fd = 2;
3586 while (!isatty(fd))
3587 if (--fd < 0)
3588 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003589 }
3590 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003591 if (ofd >= 0)
3592 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003593 if (fd < 0)
3594 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003595 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003596 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003597 do { /* while we are in the background */
3598 pgrp = tcgetpgrp(fd);
3599 if (pgrp < 0) {
3600 out:
3601 ash_msg("can't access tty; job control turned off");
3602 mflag = on = 0;
3603 goto close;
3604 }
3605 if (pgrp == getpgrp())
3606 break;
3607 killpg(0, SIGTTIN);
3608 } while (1);
3609 initialpgrp = pgrp;
3610
3611 setsignal(SIGTSTP);
3612 setsignal(SIGTTOU);
3613 setsignal(SIGTTIN);
3614 pgrp = rootpid;
3615 setpgid(0, pgrp);
3616 xtcsetpgrp(fd, pgrp);
3617 } else {
3618 /* turning job control off */
3619 fd = ttyfd;
3620 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003621 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003622 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003623 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003624 setpgid(0, pgrp);
3625 setsignal(SIGTSTP);
3626 setsignal(SIGTTOU);
3627 setsignal(SIGTTIN);
3628 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003629 if (fd >= 0)
3630 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003631 fd = -1;
3632 }
3633 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003634 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003635}
3636
3637static int
3638killcmd(int argc, char **argv)
3639{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003640 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003641 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003642 do {
3643 if (argv[i][0] == '%') {
3644 struct job *jp = getjob(argv[i], 0);
3645 unsigned pid = jp->ps[0].pid;
3646 /* Enough space for ' -NNN<nul>' */
3647 argv[i] = alloca(sizeof(int)*3 + 3);
3648 /* kill_main has matching code to expect
3649 * leading space. Needed to not confuse
3650 * negative pids with "kill -SIGNAL_NO" syntax */
3651 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003652 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003653 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003654 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003655 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003656}
3657
3658static void
3659showpipe(struct job *jp, FILE *out)
3660{
3661 struct procstat *sp;
3662 struct procstat *spend;
3663
3664 spend = jp->ps + jp->nprocs;
3665 for (sp = jp->ps + 1; sp < spend; sp++)
3666 fprintf(out, " | %s", sp->cmd);
3667 outcslow('\n', out);
3668 flush_stdout_stderr();
3669}
3670
3671
3672static int
3673restartjob(struct job *jp, int mode)
3674{
3675 struct procstat *ps;
3676 int i;
3677 int status;
3678 pid_t pgid;
3679
3680 INT_OFF;
3681 if (jp->state == JOBDONE)
3682 goto out;
3683 jp->state = JOBRUNNING;
3684 pgid = jp->ps->pid;
3685 if (mode == FORK_FG)
3686 xtcsetpgrp(ttyfd, pgid);
3687 killpg(pgid, SIGCONT);
3688 ps = jp->ps;
3689 i = jp->nprocs;
3690 do {
3691 if (WIFSTOPPED(ps->status)) {
3692 ps->status = -1;
3693 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003694 ps++;
3695 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003696 out:
3697 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3698 INT_ON;
3699 return status;
3700}
3701
3702static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003703fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003704{
3705 struct job *jp;
3706 FILE *out;
3707 int mode;
3708 int retval;
3709
3710 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3711 nextopt(nullstr);
3712 argv = argptr;
3713 out = stdout;
3714 do {
3715 jp = getjob(*argv, 1);
3716 if (mode == FORK_BG) {
3717 set_curjob(jp, CUR_RUNNING);
3718 fprintf(out, "[%d] ", jobno(jp));
3719 }
3720 outstr(jp->ps->cmd, out);
3721 showpipe(jp, out);
3722 retval = restartjob(jp, mode);
3723 } while (*argv && *++argv);
3724 return retval;
3725}
3726#endif
3727
3728static int
3729sprint_status(char *s, int status, int sigonly)
3730{
3731 int col;
3732 int st;
3733
3734 col = 0;
3735 if (!WIFEXITED(status)) {
3736#if JOBS
3737 if (WIFSTOPPED(status))
3738 st = WSTOPSIG(status);
3739 else
3740#endif
3741 st = WTERMSIG(status);
3742 if (sigonly) {
3743 if (st == SIGINT || st == SIGPIPE)
3744 goto out;
3745#if JOBS
3746 if (WIFSTOPPED(status))
3747 goto out;
3748#endif
3749 }
3750 st &= 0x7f;
3751 col = fmtstr(s, 32, strsignal(st));
3752 if (WCOREDUMP(status)) {
3753 col += fmtstr(s + col, 16, " (core dumped)");
3754 }
3755 } else if (!sigonly) {
3756 st = WEXITSTATUS(status);
3757 if (st)
3758 col = fmtstr(s, 16, "Done(%d)", st);
3759 else
3760 col = fmtstr(s, 16, "Done");
3761 }
3762 out:
3763 return col;
3764}
3765
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003766static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003767dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003768{
3769 int pid;
3770 int status;
3771 struct job *jp;
3772 struct job *thisjob;
3773 int state;
3774
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003775 TRACE(("dowait(0x%x) called\n", wait_flags));
3776
3777 /* Do a wait system call. If job control is compiled in, we accept
3778 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3779 * NB: _not_ safe_waitpid, we need to detect EINTR */
3780 pid = waitpid(-1, &status,
3781 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
3782 TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
3783
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003784 if (pid <= 0) {
3785 /* If we were doing blocking wait and (probably) got EINTR,
3786 * check for pending sigs received while waiting.
3787 * (NB: can be moved into callers if needed) */
3788 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3789 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003791 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003792 INT_OFF;
3793 thisjob = NULL;
3794 for (jp = curjob; jp; jp = jp->prev_job) {
3795 struct procstat *sp;
3796 struct procstat *spend;
3797 if (jp->state == JOBDONE)
3798 continue;
3799 state = JOBDONE;
3800 spend = jp->ps + jp->nprocs;
3801 sp = jp->ps;
3802 do {
3803 if (sp->pid == pid) {
3804 TRACE(("Job %d: changing status of proc %d "
3805 "from 0x%x to 0x%x\n",
3806 jobno(jp), pid, sp->status, status));
3807 sp->status = status;
3808 thisjob = jp;
3809 }
3810 if (sp->status == -1)
3811 state = JOBRUNNING;
3812#if JOBS
3813 if (state == JOBRUNNING)
3814 continue;
3815 if (WIFSTOPPED(sp->status)) {
3816 jp->stopstatus = sp->status;
3817 state = JOBSTOPPED;
3818 }
3819#endif
3820 } while (++sp < spend);
3821 if (thisjob)
3822 goto gotjob;
3823 }
3824#if JOBS
3825 if (!WIFSTOPPED(status))
3826#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003827 jobless--;
3828 goto out;
3829
3830 gotjob:
3831 if (state != JOBRUNNING) {
3832 thisjob->changed = 1;
3833
3834 if (thisjob->state != state) {
3835 TRACE(("Job %d: changing state from %d to %d\n",
3836 jobno(thisjob), thisjob->state, state));
3837 thisjob->state = state;
3838#if JOBS
3839 if (state == JOBSTOPPED) {
3840 set_curjob(thisjob, CUR_STOPPED);
3841 }
3842#endif
3843 }
3844 }
3845
3846 out:
3847 INT_ON;
3848
3849 if (thisjob && thisjob == job) {
3850 char s[48 + 1];
3851 int len;
3852
3853 len = sprint_status(s, status, 1);
3854 if (len) {
3855 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003856 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003857 out2str(s);
3858 }
3859 }
3860 return pid;
3861}
3862
3863#if JOBS
3864static void
3865showjob(FILE *out, struct job *jp, int mode)
3866{
3867 struct procstat *ps;
3868 struct procstat *psend;
3869 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003870 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003871 char s[80];
3872
3873 ps = jp->ps;
3874
3875 if (mode & SHOW_PGID) {
3876 /* just output process (group) id of pipeline */
3877 fprintf(out, "%d\n", ps->pid);
3878 return;
3879 }
3880
3881 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003882 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883
3884 if (jp == curjob)
3885 s[col - 2] = '+';
3886 else if (curjob && jp == curjob->prev_job)
3887 s[col - 2] = '-';
3888
3889 if (mode & SHOW_PID)
3890 col += fmtstr(s + col, 16, "%d ", ps->pid);
3891
3892 psend = ps + jp->nprocs;
3893
3894 if (jp->state == JOBRUNNING) {
3895 strcpy(s + col, "Running");
3896 col += sizeof("Running") - 1;
3897 } else {
3898 int status = psend[-1].status;
3899 if (jp->state == JOBSTOPPED)
3900 status = jp->stopstatus;
3901 col += sprint_status(s + col, status, 0);
3902 }
3903
3904 goto start;
3905
3906 do {
3907 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003908 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003909 start:
3910 fprintf(out, "%s%*c%s",
3911 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3912 );
3913 if (!(mode & SHOW_PID)) {
3914 showpipe(jp, out);
3915 break;
3916 }
3917 if (++ps == psend) {
3918 outcslow('\n', out);
3919 break;
3920 }
3921 } while (1);
3922
3923 jp->changed = 0;
3924
3925 if (jp->state == JOBDONE) {
3926 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3927 freejob(jp);
3928 }
3929}
3930
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003931/*
3932 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3933 * statuses have changed since the last call to showjobs.
3934 */
3935static void
3936showjobs(FILE *out, int mode)
3937{
3938 struct job *jp;
3939
3940 TRACE(("showjobs(%x) called\n", mode));
3941
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003942 /* If not even one job changed, there is nothing to do */
3943 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944 continue;
3945
3946 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003947 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003948 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003949 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003950 }
3951}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003952
3953static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003954jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003955{
3956 int mode, m;
3957
3958 mode = 0;
3959 while ((m = nextopt("lp"))) {
3960 if (m == 'l')
3961 mode = SHOW_PID;
3962 else
3963 mode = SHOW_PGID;
3964 }
3965
3966 argv = argptr;
3967 if (*argv) {
3968 do
3969 showjob(stdout, getjob(*argv,0), mode);
3970 while (*++argv);
3971 } else
3972 showjobs(stdout, mode);
3973
3974 return 0;
3975}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003976#endif /* JOBS */
3977
3978static int
3979getstatus(struct job *job)
3980{
3981 int status;
3982 int retval;
3983
3984 status = job->ps[job->nprocs - 1].status;
3985 retval = WEXITSTATUS(status);
3986 if (!WIFEXITED(status)) {
3987#if JOBS
3988 retval = WSTOPSIG(status);
3989 if (!WIFSTOPPED(status))
3990#endif
3991 {
3992 /* XXX: limits number of signals */
3993 retval = WTERMSIG(status);
3994#if JOBS
3995 if (retval == SIGINT)
3996 job->sigint = 1;
3997#endif
3998 }
3999 retval += 128;
4000 }
4001 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4002 jobno(job), job->nprocs, status, retval));
4003 return retval;
4004}
4005
4006static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004007waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004008{
4009 struct job *job;
4010 int retval;
4011 struct job *jp;
4012
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004013// exsig++;
4014// xbarrier();
4015 if (pendingsig)
4016 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004017
4018 nextopt(nullstr);
4019 retval = 0;
4020
4021 argv = argptr;
4022 if (!*argv) {
4023 /* wait for all jobs */
4024 for (;;) {
4025 jp = curjob;
4026 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004027 if (!jp) /* no running procs */
4028 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004029 if (jp->state == JOBRUNNING)
4030 break;
4031 jp->waited = 1;
4032 jp = jp->prev_job;
4033 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004034 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004035 }
4036 }
4037
4038 retval = 127;
4039 do {
4040 if (**argv != '%') {
4041 pid_t pid = number(*argv);
4042 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004043 while (1) {
4044 if (!job)
4045 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004046 if (job->ps[job->nprocs - 1].pid == pid)
4047 break;
4048 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004049 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050 } else
4051 job = getjob(*argv, 0);
4052 /* loop until process terminated or stopped */
4053 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004054 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004055 job->waited = 1;
4056 retval = getstatus(job);
4057 repeat:
4058 ;
4059 } while (*++argv);
4060
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004061 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004062 return retval;
4063}
4064
4065static struct job *
4066growjobtab(void)
4067{
4068 size_t len;
4069 ptrdiff_t offset;
4070 struct job *jp, *jq;
4071
4072 len = njobs * sizeof(*jp);
4073 jq = jobtab;
4074 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4075
4076 offset = (char *)jp - (char *)jq;
4077 if (offset) {
4078 /* Relocate pointers */
4079 size_t l = len;
4080
4081 jq = (struct job *)((char *)jq + l);
4082 while (l) {
4083 l -= sizeof(*jp);
4084 jq--;
4085#define joff(p) ((struct job *)((char *)(p) + l))
4086#define jmove(p) (p) = (void *)((char *)(p) + offset)
4087 if (joff(jp)->ps == &jq->ps0)
4088 jmove(joff(jp)->ps);
4089 if (joff(jp)->prev_job)
4090 jmove(joff(jp)->prev_job);
4091 }
4092 if (curjob)
4093 jmove(curjob);
4094#undef joff
4095#undef jmove
4096 }
4097
4098 njobs += 4;
4099 jobtab = jp;
4100 jp = (struct job *)((char *)jp + len);
4101 jq = jp + 3;
4102 do {
4103 jq->used = 0;
4104 } while (--jq >= jp);
4105 return jp;
4106}
4107
4108/*
4109 * Return a new job structure.
4110 * Called with interrupts off.
4111 */
4112static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004113makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004114{
4115 int i;
4116 struct job *jp;
4117
4118 for (i = njobs, jp = jobtab; ; jp++) {
4119 if (--i < 0) {
4120 jp = growjobtab();
4121 break;
4122 }
4123 if (jp->used == 0)
4124 break;
4125 if (jp->state != JOBDONE || !jp->waited)
4126 continue;
4127#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004128 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004129 continue;
4130#endif
4131 freejob(jp);
4132 break;
4133 }
4134 memset(jp, 0, sizeof(*jp));
4135#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004136 /* jp->jobctl is a bitfield.
4137 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004138 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004139 jp->jobctl = 1;
4140#endif
4141 jp->prev_job = curjob;
4142 curjob = jp;
4143 jp->used = 1;
4144 jp->ps = &jp->ps0;
4145 if (nprocs > 1) {
4146 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4147 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004148 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004149 jobno(jp)));
4150 return jp;
4151}
4152
4153#if JOBS
4154/*
4155 * Return a string identifying a command (to be printed by the
4156 * jobs command).
4157 */
4158static char *cmdnextc;
4159
4160static void
4161cmdputs(const char *s)
4162{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004163 static const char vstype[VSTYPE + 1][3] = {
4164 "", "}", "-", "+", "?", "=",
4165 "%", "%%", "#", "##"
4166 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4167 };
4168
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004169 const char *p, *str;
4170 char c, cc[2] = " ";
4171 char *nextc;
4172 int subtype = 0;
4173 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004174
4175 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4176 p = s;
4177 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004178 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179 switch (c) {
4180 case CTLESC:
4181 c = *p++;
4182 break;
4183 case CTLVAR:
4184 subtype = *p++;
4185 if ((subtype & VSTYPE) == VSLENGTH)
4186 str = "${#";
4187 else
4188 str = "${";
4189 if (!(subtype & VSQUOTE) == !(quoted & 1))
4190 goto dostr;
4191 quoted ^= 1;
4192 c = '"';
4193 break;
4194 case CTLENDVAR:
4195 str = "\"}" + !(quoted & 1);
4196 quoted >>= 1;
4197 subtype = 0;
4198 goto dostr;
4199 case CTLBACKQ:
4200 str = "$(...)";
4201 goto dostr;
4202 case CTLBACKQ+CTLQUOTE:
4203 str = "\"$(...)\"";
4204 goto dostr;
4205#if ENABLE_ASH_MATH_SUPPORT
4206 case CTLARI:
4207 str = "$((";
4208 goto dostr;
4209 case CTLENDARI:
4210 str = "))";
4211 goto dostr;
4212#endif
4213 case CTLQUOTEMARK:
4214 quoted ^= 1;
4215 c = '"';
4216 break;
4217 case '=':
4218 if (subtype == 0)
4219 break;
4220 if ((subtype & VSTYPE) != VSNORMAL)
4221 quoted <<= 1;
4222 str = vstype[subtype & VSTYPE];
4223 if (subtype & VSNUL)
4224 c = ':';
4225 else
4226 goto checkstr;
4227 break;
4228 case '\'':
4229 case '\\':
4230 case '"':
4231 case '$':
4232 /* These can only happen inside quotes */
4233 cc[0] = c;
4234 str = cc;
4235 c = '\\';
4236 break;
4237 default:
4238 break;
4239 }
4240 USTPUTC(c, nextc);
4241 checkstr:
4242 if (!str)
4243 continue;
4244 dostr:
4245 while ((c = *str++)) {
4246 USTPUTC(c, nextc);
4247 }
4248 }
4249 if (quoted & 1) {
4250 USTPUTC('"', nextc);
4251 }
4252 *nextc = 0;
4253 cmdnextc = nextc;
4254}
4255
4256/* cmdtxt() and cmdlist() call each other */
4257static void cmdtxt(union node *n);
4258
4259static void
4260cmdlist(union node *np, int sep)
4261{
4262 for (; np; np = np->narg.next) {
4263 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004264 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004265 cmdtxt(np);
4266 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004267 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004268 }
4269}
4270
4271static void
4272cmdtxt(union node *n)
4273{
4274 union node *np;
4275 struct nodelist *lp;
4276 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004277
4278 if (!n)
4279 return;
4280 switch (n->type) {
4281 default:
4282#if DEBUG
4283 abort();
4284#endif
4285 case NPIPE:
4286 lp = n->npipe.cmdlist;
4287 for (;;) {
4288 cmdtxt(lp->n);
4289 lp = lp->next;
4290 if (!lp)
4291 break;
4292 cmdputs(" | ");
4293 }
4294 break;
4295 case NSEMI:
4296 p = "; ";
4297 goto binop;
4298 case NAND:
4299 p = " && ";
4300 goto binop;
4301 case NOR:
4302 p = " || ";
4303 binop:
4304 cmdtxt(n->nbinary.ch1);
4305 cmdputs(p);
4306 n = n->nbinary.ch2;
4307 goto donode;
4308 case NREDIR:
4309 case NBACKGND:
4310 n = n->nredir.n;
4311 goto donode;
4312 case NNOT:
4313 cmdputs("!");
4314 n = n->nnot.com;
4315 donode:
4316 cmdtxt(n);
4317 break;
4318 case NIF:
4319 cmdputs("if ");
4320 cmdtxt(n->nif.test);
4321 cmdputs("; then ");
4322 n = n->nif.ifpart;
4323 if (n->nif.elsepart) {
4324 cmdtxt(n);
4325 cmdputs("; else ");
4326 n = n->nif.elsepart;
4327 }
4328 p = "; fi";
4329 goto dotail;
4330 case NSUBSHELL:
4331 cmdputs("(");
4332 n = n->nredir.n;
4333 p = ")";
4334 goto dotail;
4335 case NWHILE:
4336 p = "while ";
4337 goto until;
4338 case NUNTIL:
4339 p = "until ";
4340 until:
4341 cmdputs(p);
4342 cmdtxt(n->nbinary.ch1);
4343 n = n->nbinary.ch2;
4344 p = "; done";
4345 dodo:
4346 cmdputs("; do ");
4347 dotail:
4348 cmdtxt(n);
4349 goto dotail2;
4350 case NFOR:
4351 cmdputs("for ");
4352 cmdputs(n->nfor.var);
4353 cmdputs(" in ");
4354 cmdlist(n->nfor.args, 1);
4355 n = n->nfor.body;
4356 p = "; done";
4357 goto dodo;
4358 case NDEFUN:
4359 cmdputs(n->narg.text);
4360 p = "() { ... }";
4361 goto dotail2;
4362 case NCMD:
4363 cmdlist(n->ncmd.args, 1);
4364 cmdlist(n->ncmd.redirect, 0);
4365 break;
4366 case NARG:
4367 p = n->narg.text;
4368 dotail2:
4369 cmdputs(p);
4370 break;
4371 case NHERE:
4372 case NXHERE:
4373 p = "<<...";
4374 goto dotail2;
4375 case NCASE:
4376 cmdputs("case ");
4377 cmdputs(n->ncase.expr->narg.text);
4378 cmdputs(" in ");
4379 for (np = n->ncase.cases; np; np = np->nclist.next) {
4380 cmdtxt(np->nclist.pattern);
4381 cmdputs(") ");
4382 cmdtxt(np->nclist.body);
4383 cmdputs(";; ");
4384 }
4385 p = "esac";
4386 goto dotail2;
4387 case NTO:
4388 p = ">";
4389 goto redir;
4390 case NCLOBBER:
4391 p = ">|";
4392 goto redir;
4393 case NAPPEND:
4394 p = ">>";
4395 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004396#if ENABLE_ASH_BASH_COMPAT
4397 case NTO2:
4398#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004399 case NTOFD:
4400 p = ">&";
4401 goto redir;
4402 case NFROM:
4403 p = "<";
4404 goto redir;
4405 case NFROMFD:
4406 p = "<&";
4407 goto redir;
4408 case NFROMTO:
4409 p = "<>";
4410 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004411 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004412 cmdputs(p);
4413 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004414 cmdputs(utoa(n->ndup.dupfd));
4415 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004416 }
4417 n = n->nfile.fname;
4418 goto donode;
4419 }
4420}
4421
4422static char *
4423commandtext(union node *n)
4424{
4425 char *name;
4426
4427 STARTSTACKSTR(cmdnextc);
4428 cmdtxt(n);
4429 name = stackblock();
4430 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4431 name, cmdnextc, cmdnextc));
4432 return ckstrdup(name);
4433}
4434#endif /* JOBS */
4435
4436/*
4437 * Fork off a subshell. If we are doing job control, give the subshell its
4438 * own process group. Jp is a job structure that the job is to be added to.
4439 * N is the command that will be evaluated by the child. Both jp and n may
4440 * be NULL. The mode parameter can be one of the following:
4441 * FORK_FG - Fork off a foreground process.
4442 * FORK_BG - Fork off a background process.
4443 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4444 * process group even if job control is on.
4445 *
4446 * When job control is turned off, background processes have their standard
4447 * input redirected to /dev/null (except for the second and later processes
4448 * in a pipeline).
4449 *
4450 * Called with interrupts off.
4451 */
4452/*
4453 * Clear traps on a fork.
4454 */
4455static void
4456clear_traps(void)
4457{
4458 char **tp;
4459
4460 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004461 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004462 INT_OFF;
4463 free(*tp);
4464 *tp = NULL;
4465 if (tp != &trap[0])
4466 setsignal(tp - trap);
4467 INT_ON;
4468 }
4469 }
4470}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004471
4472/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004473static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004474
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004475/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004476static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004477forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004478{
4479 int oldlvl;
4480
4481 TRACE(("Child shell %d\n", getpid()));
4482 oldlvl = shlvl;
4483 shlvl++;
4484
4485 closescript();
4486 clear_traps();
4487#if JOBS
4488 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004489 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004490 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4491 pid_t pgrp;
4492
4493 if (jp->nprocs == 0)
4494 pgrp = getpid();
4495 else
4496 pgrp = jp->ps[0].pid;
4497 /* This can fail because we are doing it in the parent also */
4498 (void)setpgid(0, pgrp);
4499 if (mode == FORK_FG)
4500 xtcsetpgrp(ttyfd, pgrp);
4501 setsignal(SIGTSTP);
4502 setsignal(SIGTTOU);
4503 } else
4504#endif
4505 if (mode == FORK_BG) {
4506 ignoresig(SIGINT);
4507 ignoresig(SIGQUIT);
4508 if (jp->nprocs == 0) {
4509 close(0);
4510 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004511 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004512 }
4513 }
4514 if (!oldlvl && iflag) {
4515 setsignal(SIGINT);
4516 setsignal(SIGQUIT);
4517 setsignal(SIGTERM);
4518 }
4519 for (jp = curjob; jp; jp = jp->prev_job)
4520 freejob(jp);
4521 jobless = 0;
4522}
4523
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004524/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004525#if !JOBS
4526#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4527#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004528static void
4529forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4530{
4531 TRACE(("In parent shell: child = %d\n", pid));
4532 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004533 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4534 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004535 jobless++;
4536 return;
4537 }
4538#if JOBS
4539 if (mode != FORK_NOJOB && jp->jobctl) {
4540 int pgrp;
4541
4542 if (jp->nprocs == 0)
4543 pgrp = pid;
4544 else
4545 pgrp = jp->ps[0].pid;
4546 /* This can fail because we are doing it in the child also */
4547 setpgid(pid, pgrp);
4548 }
4549#endif
4550 if (mode == FORK_BG) {
4551 backgndpid = pid; /* set $! */
4552 set_curjob(jp, CUR_RUNNING);
4553 }
4554 if (jp) {
4555 struct procstat *ps = &jp->ps[jp->nprocs++];
4556 ps->pid = pid;
4557 ps->status = -1;
4558 ps->cmd = nullstr;
4559#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004560 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004561 ps->cmd = commandtext(n);
4562#endif
4563 }
4564}
4565
4566static int
4567forkshell(struct job *jp, union node *n, int mode)
4568{
4569 int pid;
4570
4571 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4572 pid = fork();
4573 if (pid < 0) {
4574 TRACE(("Fork failed, errno=%d", errno));
4575 if (jp)
4576 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004577 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004578 }
4579 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004580 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004581 else
4582 forkparent(jp, n, mode, pid);
4583 return pid;
4584}
4585
4586/*
4587 * Wait for job to finish.
4588 *
4589 * Under job control we have the problem that while a child process is
4590 * running interrupts generated by the user are sent to the child but not
4591 * to the shell. This means that an infinite loop started by an inter-
4592 * active user may be hard to kill. With job control turned off, an
4593 * interactive user may place an interactive program inside a loop. If
4594 * the interactive program catches interrupts, the user doesn't want
4595 * these interrupts to also abort the loop. The approach we take here
4596 * is to have the shell ignore interrupt signals while waiting for a
4597 * foreground process to terminate, and then send itself an interrupt
4598 * signal if the child process was terminated by an interrupt signal.
4599 * Unfortunately, some programs want to do a bit of cleanup and then
4600 * exit on interrupt; unless these processes terminate themselves by
4601 * sending a signal to themselves (instead of calling exit) they will
4602 * confuse this approach.
4603 *
4604 * Called with interrupts off.
4605 */
4606static int
4607waitforjob(struct job *jp)
4608{
4609 int st;
4610
4611 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4612 while (jp->state == JOBRUNNING) {
4613 dowait(DOWAIT_BLOCK, jp);
4614 }
4615 st = getstatus(jp);
4616#if JOBS
4617 if (jp->jobctl) {
4618 xtcsetpgrp(ttyfd, rootpid);
4619 /*
4620 * This is truly gross.
4621 * If we're doing job control, then we did a TIOCSPGRP which
4622 * caused us (the shell) to no longer be in the controlling
4623 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4624 * intuit from the subprocess exit status whether a SIGINT
4625 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4626 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004627 if (jp->sigint) /* TODO: do the same with all signals */
4628 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004629 }
4630 if (jp->state == JOBDONE)
4631#endif
4632 freejob(jp);
4633 return st;
4634}
4635
4636/*
4637 * return 1 if there are stopped jobs, otherwise 0
4638 */
4639static int
4640stoppedjobs(void)
4641{
4642 struct job *jp;
4643 int retval;
4644
4645 retval = 0;
4646 if (job_warning)
4647 goto out;
4648 jp = curjob;
4649 if (jp && jp->state == JOBSTOPPED) {
4650 out2str("You have stopped jobs.\n");
4651 job_warning = 2;
4652 retval++;
4653 }
4654 out:
4655 return retval;
4656}
4657
4658
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004659/* ============ redir.c
4660 *
4661 * Code for dealing with input/output redirection.
4662 */
4663
4664#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004665#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004666
4667/*
4668 * Open a file in noclobber mode.
4669 * The code was copied from bash.
4670 */
4671static int
4672noclobberopen(const char *fname)
4673{
4674 int r, fd;
4675 struct stat finfo, finfo2;
4676
4677 /*
4678 * If the file exists and is a regular file, return an error
4679 * immediately.
4680 */
4681 r = stat(fname, &finfo);
4682 if (r == 0 && S_ISREG(finfo.st_mode)) {
4683 errno = EEXIST;
4684 return -1;
4685 }
4686
4687 /*
4688 * If the file was not present (r != 0), make sure we open it
4689 * exclusively so that if it is created before we open it, our open
4690 * will fail. Make sure that we do not truncate an existing file.
4691 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4692 * file was not a regular file, we leave O_EXCL off.
4693 */
4694 if (r != 0)
4695 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4696 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4697
4698 /* If the open failed, return the file descriptor right away. */
4699 if (fd < 0)
4700 return fd;
4701
4702 /*
4703 * OK, the open succeeded, but the file may have been changed from a
4704 * non-regular file to a regular file between the stat and the open.
4705 * We are assuming that the O_EXCL open handles the case where FILENAME
4706 * did not exist and is symlinked to an existing file between the stat
4707 * and open.
4708 */
4709
4710 /*
4711 * If we can open it and fstat the file descriptor, and neither check
4712 * revealed that it was a regular file, and the file has not been
4713 * replaced, return the file descriptor.
4714 */
4715 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4716 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4717 return fd;
4718
4719 /* The file has been replaced. badness. */
4720 close(fd);
4721 errno = EEXIST;
4722 return -1;
4723}
4724
4725/*
4726 * Handle here documents. Normally we fork off a process to write the
4727 * data to a pipe. If the document is short, we can stuff the data in
4728 * the pipe without forking.
4729 */
4730/* openhere needs this forward reference */
4731static void expandhere(union node *arg, int fd);
4732static int
4733openhere(union node *redir)
4734{
4735 int pip[2];
4736 size_t len = 0;
4737
4738 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004739 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004740 if (redir->type == NHERE) {
4741 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004742 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004743 full_write(pip[1], redir->nhere.doc->narg.text, len);
4744 goto out;
4745 }
4746 }
4747 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004748 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004749 close(pip[0]);
4750 signal(SIGINT, SIG_IGN);
4751 signal(SIGQUIT, SIG_IGN);
4752 signal(SIGHUP, SIG_IGN);
4753#ifdef SIGTSTP
4754 signal(SIGTSTP, SIG_IGN);
4755#endif
4756 signal(SIGPIPE, SIG_DFL);
4757 if (redir->type == NHERE)
4758 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004759 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004760 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004761 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004762 }
4763 out:
4764 close(pip[1]);
4765 return pip[0];
4766}
4767
4768static int
4769openredirect(union node *redir)
4770{
4771 char *fname;
4772 int f;
4773
4774 switch (redir->nfile.type) {
4775 case NFROM:
4776 fname = redir->nfile.expfname;
4777 f = open(fname, O_RDONLY);
4778 if (f < 0)
4779 goto eopen;
4780 break;
4781 case NFROMTO:
4782 fname = redir->nfile.expfname;
4783 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4784 if (f < 0)
4785 goto ecreate;
4786 break;
4787 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004788#if ENABLE_ASH_BASH_COMPAT
4789 case NTO2:
4790#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004791 /* Take care of noclobber mode. */
4792 if (Cflag) {
4793 fname = redir->nfile.expfname;
4794 f = noclobberopen(fname);
4795 if (f < 0)
4796 goto ecreate;
4797 break;
4798 }
4799 /* FALLTHROUGH */
4800 case NCLOBBER:
4801 fname = redir->nfile.expfname;
4802 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4803 if (f < 0)
4804 goto ecreate;
4805 break;
4806 case NAPPEND:
4807 fname = redir->nfile.expfname;
4808 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4809 if (f < 0)
4810 goto ecreate;
4811 break;
4812 default:
4813#if DEBUG
4814 abort();
4815#endif
4816 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004817/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004818// case NTOFD:
4819// case NFROMFD:
4820// f = -1;
4821// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004822 case NHERE:
4823 case NXHERE:
4824 f = openhere(redir);
4825 break;
4826 }
4827
4828 return f;
4829 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004830 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004831 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004832 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004833}
4834
4835/*
4836 * Copy a file descriptor to be >= to. Returns -1
4837 * if the source file descriptor is closed, EMPTY if there are no unused
4838 * file descriptors left.
4839 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004840/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4841 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004842enum {
4843 COPYFD_EXACT = (int)~(INT_MAX),
4844 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4845};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004846static int
4847copyfd(int from, int to)
4848{
4849 int newfd;
4850
Denis Vlasenko5a867312008-07-24 19:46:38 +00004851 if (to & COPYFD_EXACT) {
4852 to &= ~COPYFD_EXACT;
4853 /*if (from != to)*/
4854 newfd = dup2(from, to);
4855 } else {
4856 newfd = fcntl(from, F_DUPFD, to);
4857 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004858 if (newfd < 0) {
4859 if (errno == EMFILE)
4860 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004861 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004862 ash_msg_and_raise_error("%d: %m", from);
4863 }
4864 return newfd;
4865}
4866
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004867/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004868struct two_fd_t {
4869 int orig, copy;
4870};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004871struct redirtab {
4872 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004873 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004874 int pair_count;
4875 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004876};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004877#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004878
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004879static int need_to_remember(struct redirtab *rp, int fd)
4880{
4881 int i;
4882
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004883 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004884 return 0;
4885
4886 for (i = 0; i < rp->pair_count; i++) {
4887 if (rp->two_fd[i].orig == fd) {
4888 /* already remembered */
4889 return 0;
4890 }
4891 }
4892 return 1;
4893}
4894
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004895/* "hidden" fd is a fd used to read scripts, or a copy of such */
4896static int is_hidden_fd(struct redirtab *rp, int fd)
4897{
4898 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004899 struct parsefile *pf;
4900
4901 if (fd == -1)
4902 return 0;
4903 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004904 while (pf) {
4905 if (fd == pf->fd) {
4906 return 1;
4907 }
4908 pf = pf->prev;
4909 }
4910 if (!rp)
4911 return 0;
4912 fd |= COPYFD_RESTORE;
4913 for (i = 0; i < rp->pair_count; i++) {
4914 if (rp->two_fd[i].copy == fd) {
4915 return 1;
4916 }
4917 }
4918 return 0;
4919}
4920
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004921/*
4922 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4923 * old file descriptors are stashed away so that the redirection can be
4924 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4925 * standard output, and the standard error if it becomes a duplicate of
4926 * stdout, is saved in memory.
4927 */
4928/* flags passed to redirect */
4929#define REDIR_PUSH 01 /* save previous values of file descriptors */
4930#define REDIR_SAVEFD2 03 /* set preverrout */
4931static void
4932redirect(union node *redir, int flags)
4933{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004934 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004935 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004936 int i;
4937 int fd;
4938 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004939 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004940
Denis Vlasenko01631112007-12-16 17:20:38 +00004941 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004942 if (!redir) {
4943 return;
4944 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004945
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004946 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004947 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004948 INT_OFF;
4949 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004950 union node *tmp = redir;
4951 do {
4952 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004953#if ENABLE_ASH_BASH_COMPAT
4954 if (redir->nfile.type == NTO2)
4955 sv_pos++;
4956#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004957 tmp = tmp->nfile.next;
4958 } while (tmp);
4959 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004960 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004961 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004962 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004963 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004964 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004965 while (sv_pos > 0) {
4966 sv_pos--;
4967 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
4968 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004969 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004970
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004971 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00004972 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004973 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004974 int right_fd = redir->ndup.dupfd;
4975 /* redirect from/to same file descriptor? */
4976 if (right_fd == fd)
4977 continue;
4978 /* echo >&10 and 10 is a fd opened to the sh script? */
4979 if (is_hidden_fd(sv, right_fd)) {
4980 errno = EBADF; /* as if it is closed */
4981 ash_msg_and_raise_error("%d: %m", right_fd);
4982 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00004983 newfd = -1;
4984 } else {
4985 newfd = openredirect(redir); /* always >= 0 */
4986 if (fd == newfd) {
4987 /* Descriptor wasn't open before redirect.
4988 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004989 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00004990 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004991 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00004992 continue;
4993 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004994 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00004995#if ENABLE_ASH_BASH_COMPAT
4996 redirect_more:
4997#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004998 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004999 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005000 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005001/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5002 * are closed in popredir() in the child, preventing them from leaking
5003 * into child. (popredir() also cleans up the mess in case of failures)
5004 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005005 if (i == -1) {
5006 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005007 if (i != EBADF) {
5008 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005009 if (newfd >= 0)
5010 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005011 errno = i;
5012 ash_msg_and_raise_error("%d: %m", fd);
5013 /* NOTREACHED */
5014 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005015 /* EBADF: it is not open - good, remember to close it */
5016 remember_to_close:
5017 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005018 } else { /* fd is open, save its copy */
5019 /* "exec fd>&-" should not close fds
5020 * which point to script file(s).
5021 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005022 if (is_hidden_fd(sv, fd))
5023 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005024 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005025 if (fd == 2)
5026 copied_fd2 = i;
5027 sv->two_fd[sv_pos].orig = fd;
5028 sv->two_fd[sv_pos].copy = i;
5029 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005030 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005031 if (newfd < 0) {
5032 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005033 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005034 close(fd);
5035 } else {
5036 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005037 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005038 } else if (fd != newfd) { /* move newfd to fd */
5039 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005040#if ENABLE_ASH_BASH_COMPAT
5041 if (!(redir->nfile.type == NTO2 && fd == 2))
5042#endif
5043 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005044 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005045#if ENABLE_ASH_BASH_COMPAT
5046 if (redir->nfile.type == NTO2 && fd == 1) {
5047 /* We already redirected it to fd 1, now copy it to 2 */
5048 newfd = 1;
5049 fd = 2;
5050 goto redirect_more;
5051 }
5052#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005053 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005054
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005055 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005056 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5057 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005058}
5059
5060/*
5061 * Undo the effects of the last redirection.
5062 */
5063static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005064popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005065{
5066 struct redirtab *rp;
5067 int i;
5068
Denis Vlasenko01631112007-12-16 17:20:38 +00005069 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005070 return;
5071 INT_OFF;
5072 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005073 for (i = 0; i < rp->pair_count; i++) {
5074 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005075 int copy = rp->two_fd[i].copy;
5076 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005077 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005078 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005079 continue;
5080 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005081 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005082 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005083 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005084 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005085 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005086 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005087 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005088 }
5089 }
5090 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005091 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005092 free(rp);
5093 INT_ON;
5094}
5095
5096/*
5097 * Undo all redirections. Called on error or interrupt.
5098 */
5099
5100/*
5101 * Discard all saved file descriptors.
5102 */
5103static void
5104clearredir(int drop)
5105{
5106 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005107 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005108 if (!redirlist)
5109 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005110 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005111 }
5112}
5113
5114static int
5115redirectsafe(union node *redir, int flags)
5116{
5117 int err;
5118 volatile int saveint;
5119 struct jmploc *volatile savehandler = exception_handler;
5120 struct jmploc jmploc;
5121
5122 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005123 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5124 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005125 if (!err) {
5126 exception_handler = &jmploc;
5127 redirect(redir, flags);
5128 }
5129 exception_handler = savehandler;
5130 if (err && exception != EXERROR)
5131 longjmp(exception_handler->loc, 1);
5132 RESTORE_INT(saveint);
5133 return err;
5134}
5135
5136
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005137/* ============ Routines to expand arguments to commands
5138 *
5139 * We have to deal with backquotes, shell variables, and file metacharacters.
5140 */
5141
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005142#if ENABLE_ASH_MATH_SUPPORT_64
5143typedef int64_t arith_t;
5144#define arith_t_type long long
5145#else
5146typedef long arith_t;
5147#define arith_t_type long
5148#endif
5149
5150#if ENABLE_ASH_MATH_SUPPORT
5151static arith_t dash_arith(const char *);
5152static arith_t arith(const char *expr, int *perrcode);
5153#endif
5154
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005155/*
5156 * expandarg flags
5157 */
5158#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5159#define EXP_TILDE 0x2 /* do normal tilde expansion */
5160#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5161#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5162#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5163#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5164#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5165#define EXP_WORD 0x80 /* expand word in parameter expansion */
5166#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5167/*
5168 * _rmescape() flags
5169 */
5170#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5171#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5172#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5173#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5174#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5175
5176/*
5177 * Structure specifying which parts of the string should be searched
5178 * for IFS characters.
5179 */
5180struct ifsregion {
5181 struct ifsregion *next; /* next region in list */
5182 int begoff; /* offset of start of region */
5183 int endoff; /* offset of end of region */
5184 int nulonly; /* search for nul bytes only */
5185};
5186
5187struct arglist {
5188 struct strlist *list;
5189 struct strlist **lastp;
5190};
5191
5192/* output of current string */
5193static char *expdest;
5194/* list of back quote expressions */
5195static struct nodelist *argbackq;
5196/* first struct in list of ifs regions */
5197static struct ifsregion ifsfirst;
5198/* last struct in list */
5199static struct ifsregion *ifslastp;
5200/* holds expanded arg list */
5201static struct arglist exparg;
5202
5203/*
5204 * Our own itoa().
5205 */
5206static int
5207cvtnum(arith_t num)
5208{
5209 int len;
5210
5211 expdest = makestrspace(32, expdest);
5212#if ENABLE_ASH_MATH_SUPPORT_64
5213 len = fmtstr(expdest, 32, "%lld", (long long) num);
5214#else
5215 len = fmtstr(expdest, 32, "%ld", num);
5216#endif
5217 STADJUST(len, expdest);
5218 return len;
5219}
5220
5221static size_t
5222esclen(const char *start, const char *p)
5223{
5224 size_t esc = 0;
5225
5226 while (p > start && *--p == CTLESC) {
5227 esc++;
5228 }
5229 return esc;
5230}
5231
5232/*
5233 * Remove any CTLESC characters from a string.
5234 */
5235static char *
5236_rmescapes(char *str, int flag)
5237{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005238 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005239
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005240 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005241 unsigned inquotes;
5242 int notescaped;
5243 int globbing;
5244
5245 p = strpbrk(str, qchars);
5246 if (!p) {
5247 return str;
5248 }
5249 q = p;
5250 r = str;
5251 if (flag & RMESCAPE_ALLOC) {
5252 size_t len = p - str;
5253 size_t fulllen = len + strlen(p) + 1;
5254
5255 if (flag & RMESCAPE_GROW) {
5256 r = makestrspace(fulllen, expdest);
5257 } else if (flag & RMESCAPE_HEAP) {
5258 r = ckmalloc(fulllen);
5259 } else {
5260 r = stalloc(fulllen);
5261 }
5262 q = r;
5263 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005264 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005265 }
5266 }
5267 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5268 globbing = flag & RMESCAPE_GLOB;
5269 notescaped = globbing;
5270 while (*p) {
5271 if (*p == CTLQUOTEMARK) {
5272 inquotes = ~inquotes;
5273 p++;
5274 notescaped = globbing;
5275 continue;
5276 }
5277 if (*p == '\\') {
5278 /* naked back slash */
5279 notescaped = 0;
5280 goto copy;
5281 }
5282 if (*p == CTLESC) {
5283 p++;
5284 if (notescaped && inquotes && *p != '/') {
5285 *q++ = '\\';
5286 }
5287 }
5288 notescaped = globbing;
5289 copy:
5290 *q++ = *p++;
5291 }
5292 *q = '\0';
5293 if (flag & RMESCAPE_GROW) {
5294 expdest = r;
5295 STADJUST(q - r + 1, expdest);
5296 }
5297 return r;
5298}
5299#define rmescapes(p) _rmescapes((p), 0)
5300
5301#define pmatch(a, b) !fnmatch((a), (b), 0)
5302
5303/*
5304 * Prepare a pattern for a expmeta (internal glob(3)) call.
5305 *
5306 * Returns an stalloced string.
5307 */
5308static char *
5309preglob(const char *pattern, int quoted, int flag)
5310{
5311 flag |= RMESCAPE_GLOB;
5312 if (quoted) {
5313 flag |= RMESCAPE_QUOTED;
5314 }
5315 return _rmescapes((char *)pattern, flag);
5316}
5317
5318/*
5319 * Put a string on the stack.
5320 */
5321static void
5322memtodest(const char *p, size_t len, int syntax, int quotes)
5323{
5324 char *q = expdest;
5325
5326 q = makestrspace(len * 2, q);
5327
5328 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005329 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005330 if (!c)
5331 continue;
5332 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5333 USTPUTC(CTLESC, q);
5334 USTPUTC(c, q);
5335 }
5336
5337 expdest = q;
5338}
5339
5340static void
5341strtodest(const char *p, int syntax, int quotes)
5342{
5343 memtodest(p, strlen(p), syntax, quotes);
5344}
5345
5346/*
5347 * Record the fact that we have to scan this region of the
5348 * string for IFS characters.
5349 */
5350static void
5351recordregion(int start, int end, int nulonly)
5352{
5353 struct ifsregion *ifsp;
5354
5355 if (ifslastp == NULL) {
5356 ifsp = &ifsfirst;
5357 } else {
5358 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005359 ifsp = ckzalloc(sizeof(*ifsp));
5360 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005361 ifslastp->next = ifsp;
5362 INT_ON;
5363 }
5364 ifslastp = ifsp;
5365 ifslastp->begoff = start;
5366 ifslastp->endoff = end;
5367 ifslastp->nulonly = nulonly;
5368}
5369
5370static void
5371removerecordregions(int endoff)
5372{
5373 if (ifslastp == NULL)
5374 return;
5375
5376 if (ifsfirst.endoff > endoff) {
5377 while (ifsfirst.next != NULL) {
5378 struct ifsregion *ifsp;
5379 INT_OFF;
5380 ifsp = ifsfirst.next->next;
5381 free(ifsfirst.next);
5382 ifsfirst.next = ifsp;
5383 INT_ON;
5384 }
5385 if (ifsfirst.begoff > endoff)
5386 ifslastp = NULL;
5387 else {
5388 ifslastp = &ifsfirst;
5389 ifsfirst.endoff = endoff;
5390 }
5391 return;
5392 }
5393
5394 ifslastp = &ifsfirst;
5395 while (ifslastp->next && ifslastp->next->begoff < endoff)
5396 ifslastp=ifslastp->next;
5397 while (ifslastp->next != NULL) {
5398 struct ifsregion *ifsp;
5399 INT_OFF;
5400 ifsp = ifslastp->next->next;
5401 free(ifslastp->next);
5402 ifslastp->next = ifsp;
5403 INT_ON;
5404 }
5405 if (ifslastp->endoff > endoff)
5406 ifslastp->endoff = endoff;
5407}
5408
5409static char *
5410exptilde(char *startp, char *p, int flag)
5411{
5412 char c;
5413 char *name;
5414 struct passwd *pw;
5415 const char *home;
5416 int quotes = flag & (EXP_FULL | EXP_CASE);
5417 int startloc;
5418
5419 name = p + 1;
5420
5421 while ((c = *++p) != '\0') {
5422 switch (c) {
5423 case CTLESC:
5424 return startp;
5425 case CTLQUOTEMARK:
5426 return startp;
5427 case ':':
5428 if (flag & EXP_VARTILDE)
5429 goto done;
5430 break;
5431 case '/':
5432 case CTLENDVAR:
5433 goto done;
5434 }
5435 }
5436 done:
5437 *p = '\0';
5438 if (*name == '\0') {
5439 home = lookupvar(homestr);
5440 } else {
5441 pw = getpwnam(name);
5442 if (pw == NULL)
5443 goto lose;
5444 home = pw->pw_dir;
5445 }
5446 if (!home || !*home)
5447 goto lose;
5448 *p = c;
5449 startloc = expdest - (char *)stackblock();
5450 strtodest(home, SQSYNTAX, quotes);
5451 recordregion(startloc, expdest - (char *)stackblock(), 0);
5452 return p;
5453 lose:
5454 *p = c;
5455 return startp;
5456}
5457
5458/*
5459 * Execute a command inside back quotes. If it's a builtin command, we
5460 * want to save its output in a block obtained from malloc. Otherwise
5461 * we fork off a subprocess and get the output of the command via a pipe.
5462 * Should be called with interrupts off.
5463 */
5464struct backcmd { /* result of evalbackcmd */
5465 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005466 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005467 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005468 struct job *jp; /* job structure for command */
5469};
5470
5471/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005472static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005473#define EV_EXIT 01 /* exit after evaluating tree */
5474static void evaltree(union node *, int);
5475
5476static void
5477evalbackcmd(union node *n, struct backcmd *result)
5478{
5479 int saveherefd;
5480
5481 result->fd = -1;
5482 result->buf = NULL;
5483 result->nleft = 0;
5484 result->jp = NULL;
5485 if (n == NULL) {
5486 goto out;
5487 }
5488
5489 saveherefd = herefd;
5490 herefd = -1;
5491
5492 {
5493 int pip[2];
5494 struct job *jp;
5495
5496 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005497 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005498 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005499 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5500 FORCE_INT_ON;
5501 close(pip[0]);
5502 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005503 /*close(1);*/
5504 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005505 close(pip[1]);
5506 }
5507 eflag = 0;
5508 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5509 /* NOTREACHED */
5510 }
5511 close(pip[1]);
5512 result->fd = pip[0];
5513 result->jp = jp;
5514 }
5515 herefd = saveherefd;
5516 out:
5517 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5518 result->fd, result->buf, result->nleft, result->jp));
5519}
5520
5521/*
5522 * Expand stuff in backwards quotes.
5523 */
5524static void
5525expbackq(union node *cmd, int quoted, int quotes)
5526{
5527 struct backcmd in;
5528 int i;
5529 char buf[128];
5530 char *p;
5531 char *dest;
5532 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005533 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005534 struct stackmark smark;
5535
5536 INT_OFF;
5537 setstackmark(&smark);
5538 dest = expdest;
5539 startloc = dest - (char *)stackblock();
5540 grabstackstr(dest);
5541 evalbackcmd(cmd, &in);
5542 popstackmark(&smark);
5543
5544 p = in.buf;
5545 i = in.nleft;
5546 if (i == 0)
5547 goto read;
5548 for (;;) {
5549 memtodest(p, i, syntax, quotes);
5550 read:
5551 if (in.fd < 0)
5552 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005553 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005554 TRACE(("expbackq: read returns %d\n", i));
5555 if (i <= 0)
5556 break;
5557 p = buf;
5558 }
5559
Denis Vlasenko60818682007-09-28 22:07:23 +00005560 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005561 if (in.fd >= 0) {
5562 close(in.fd);
5563 back_exitstatus = waitforjob(in.jp);
5564 }
5565 INT_ON;
5566
5567 /* Eat all trailing newlines */
5568 dest = expdest;
5569 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5570 STUNPUTC(dest);
5571 expdest = dest;
5572
5573 if (quoted == 0)
5574 recordregion(startloc, dest - (char *)stackblock(), 0);
5575 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5576 (dest - (char *)stackblock()) - startloc,
5577 (dest - (char *)stackblock()) - startloc,
5578 stackblock() + startloc));
5579}
5580
5581#if ENABLE_ASH_MATH_SUPPORT
5582/*
5583 * Expand arithmetic expression. Backup to start of expression,
5584 * evaluate, place result in (backed up) result, adjust string position.
5585 */
5586static void
5587expari(int quotes)
5588{
5589 char *p, *start;
5590 int begoff;
5591 int flag;
5592 int len;
5593
5594 /* ifsfree(); */
5595
5596 /*
5597 * This routine is slightly over-complicated for
5598 * efficiency. Next we scan backwards looking for the
5599 * start of arithmetic.
5600 */
5601 start = stackblock();
5602 p = expdest - 1;
5603 *p = '\0';
5604 p--;
5605 do {
5606 int esc;
5607
5608 while (*p != CTLARI) {
5609 p--;
5610#if DEBUG
5611 if (p < start) {
5612 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5613 }
5614#endif
5615 }
5616
5617 esc = esclen(start, p);
5618 if (!(esc % 2)) {
5619 break;
5620 }
5621
5622 p -= esc + 1;
5623 } while (1);
5624
5625 begoff = p - start;
5626
5627 removerecordregions(begoff);
5628
5629 flag = p[1];
5630
5631 expdest = p;
5632
5633 if (quotes)
5634 rmescapes(p + 2);
5635
5636 len = cvtnum(dash_arith(p + 2));
5637
5638 if (flag != '"')
5639 recordregion(begoff, begoff + len, 0);
5640}
5641#endif
5642
5643/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005644static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005645
5646/*
5647 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5648 * characters to allow for further processing. Otherwise treat
5649 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005650 *
5651 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5652 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5653 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005654 */
5655static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005656argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005657{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005658 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005659 '=',
5660 ':',
5661 CTLQUOTEMARK,
5662 CTLENDVAR,
5663 CTLESC,
5664 CTLVAR,
5665 CTLBACKQ,
5666 CTLBACKQ | CTLQUOTE,
5667#if ENABLE_ASH_MATH_SUPPORT
5668 CTLENDARI,
5669#endif
5670 0
5671 };
5672 const char *reject = spclchars;
5673 int c;
5674 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5675 int breakall = flag & EXP_WORD;
5676 int inquotes;
5677 size_t length;
5678 int startloc;
5679
5680 if (!(flag & EXP_VARTILDE)) {
5681 reject += 2;
5682 } else if (flag & EXP_VARTILDE2) {
5683 reject++;
5684 }
5685 inquotes = 0;
5686 length = 0;
5687 if (flag & EXP_TILDE) {
5688 char *q;
5689
5690 flag &= ~EXP_TILDE;
5691 tilde:
5692 q = p;
5693 if (*q == CTLESC && (flag & EXP_QWORD))
5694 q++;
5695 if (*q == '~')
5696 p = exptilde(p, q, flag);
5697 }
5698 start:
5699 startloc = expdest - (char *)stackblock();
5700 for (;;) {
5701 length += strcspn(p + length, reject);
5702 c = p[length];
5703 if (c && (!(c & 0x80)
5704#if ENABLE_ASH_MATH_SUPPORT
5705 || c == CTLENDARI
5706#endif
5707 )) {
5708 /* c == '=' || c == ':' || c == CTLENDARI */
5709 length++;
5710 }
5711 if (length > 0) {
5712 int newloc;
5713 expdest = stack_nputstr(p, length, expdest);
5714 newloc = expdest - (char *)stackblock();
5715 if (breakall && !inquotes && newloc > startloc) {
5716 recordregion(startloc, newloc, 0);
5717 }
5718 startloc = newloc;
5719 }
5720 p += length + 1;
5721 length = 0;
5722
5723 switch (c) {
5724 case '\0':
5725 goto breakloop;
5726 case '=':
5727 if (flag & EXP_VARTILDE2) {
5728 p--;
5729 continue;
5730 }
5731 flag |= EXP_VARTILDE2;
5732 reject++;
5733 /* fall through */
5734 case ':':
5735 /*
5736 * sort of a hack - expand tildes in variable
5737 * assignments (after the first '=' and after ':'s).
5738 */
5739 if (*--p == '~') {
5740 goto tilde;
5741 }
5742 continue;
5743 }
5744
5745 switch (c) {
5746 case CTLENDVAR: /* ??? */
5747 goto breakloop;
5748 case CTLQUOTEMARK:
5749 /* "$@" syntax adherence hack */
5750 if (
5751 !inquotes &&
5752 !memcmp(p, dolatstr, 4) &&
5753 (p[4] == CTLQUOTEMARK || (
5754 p[4] == CTLENDVAR &&
5755 p[5] == CTLQUOTEMARK
5756 ))
5757 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005758 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005759 goto start;
5760 }
5761 inquotes = !inquotes;
5762 addquote:
5763 if (quotes) {
5764 p--;
5765 length++;
5766 startloc++;
5767 }
5768 break;
5769 case CTLESC:
5770 startloc++;
5771 length++;
5772 goto addquote;
5773 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005774 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005775 goto start;
5776 case CTLBACKQ:
5777 c = 0;
5778 case CTLBACKQ|CTLQUOTE:
5779 expbackq(argbackq->n, c, quotes);
5780 argbackq = argbackq->next;
5781 goto start;
5782#if ENABLE_ASH_MATH_SUPPORT
5783 case CTLENDARI:
5784 p--;
5785 expari(quotes);
5786 goto start;
5787#endif
5788 }
5789 }
5790 breakloop:
5791 ;
5792}
5793
5794static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005795scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005796 int zero)
5797{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005798// This commented out code was added by James Simmons <jsimmons@infradead.org>
5799// as part of a larger change when he added support for ${var/a/b}.
5800// However, it broke # and % operators:
5801//
5802//var=ababcdcd
5803// ok bad
5804//echo ${var#ab} abcdcd abcdcd
5805//echo ${var##ab} abcdcd abcdcd
5806//echo ${var#a*b} abcdcd ababcdcd (!)
5807//echo ${var##a*b} cdcd cdcd
5808//echo ${var#?} babcdcd ababcdcd (!)
5809//echo ${var##?} babcdcd babcdcd
5810//echo ${var#*} ababcdcd babcdcd (!)
5811//echo ${var##*}
5812//echo ${var%cd} ababcd ababcd
5813//echo ${var%%cd} ababcd abab (!)
5814//echo ${var%c*d} ababcd ababcd
5815//echo ${var%%c*d} abab ababcdcd (!)
5816//echo ${var%?} ababcdc ababcdc
5817//echo ${var%%?} ababcdc ababcdcd (!)
5818//echo ${var%*} ababcdcd ababcdcd
5819//echo ${var%%*}
5820//
5821// Commenting it back out helped. Remove it completely if it really
5822// is not needed.
5823
5824 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005825 char c;
5826
5827 loc = startp;
5828 loc2 = rmesc;
5829 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005830 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005831 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005832
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005833 c = *loc2;
5834 if (zero) {
5835 *loc2 = '\0';
5836 s = rmesc;
5837 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005838 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005839
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005840// // chop off end if its '*'
5841// full = strrchr(str, '*');
5842// if (full && full != str)
5843// match--;
5844//
5845// // If str starts with '*' replace with s.
5846// if ((*str == '*') && strlen(s) >= match) {
5847// full = xstrdup(s);
5848// strncpy(full+strlen(s)-match+1, str+1, match-1);
5849// } else
5850// full = xstrndup(str, match);
5851// match = strncmp(s, full, strlen(full));
5852// free(full);
5853//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005854 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005855 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005856 return loc;
5857 if (quotes && *loc == CTLESC)
5858 loc++;
5859 loc++;
5860 loc2++;
5861 } while (c);
5862 return 0;
5863}
5864
5865static char *
5866scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5867 int zero)
5868{
5869 int esc = 0;
5870 char *loc;
5871 char *loc2;
5872
5873 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5874 int match;
5875 char c = *loc2;
5876 const char *s = loc2;
5877 if (zero) {
5878 *loc2 = '\0';
5879 s = rmesc;
5880 }
5881 match = pmatch(str, s);
5882 *loc2 = c;
5883 if (match)
5884 return loc;
5885 loc--;
5886 if (quotes) {
5887 if (--esc < 0) {
5888 esc = esclen(startp, loc);
5889 }
5890 if (esc % 2) {
5891 esc--;
5892 loc--;
5893 }
5894 }
5895 }
5896 return 0;
5897}
5898
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005899static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005900static void
5901varunset(const char *end, const char *var, const char *umsg, int varflags)
5902{
5903 const char *msg;
5904 const char *tail;
5905
5906 tail = nullstr;
5907 msg = "parameter not set";
5908 if (umsg) {
5909 if (*end == CTLENDVAR) {
5910 if (varflags & VSNUL)
5911 tail = " or null";
5912 } else
5913 msg = umsg;
5914 }
5915 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5916}
5917
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005918#if ENABLE_ASH_BASH_COMPAT
5919static char *
5920parse_sub_pattern(char *arg, int inquotes)
5921{
5922 char *idx, *repl = NULL;
5923 unsigned char c;
5924
Denis Vlasenko2659c632008-06-14 06:04:59 +00005925 idx = arg;
5926 while (1) {
5927 c = *arg;
5928 if (!c)
5929 break;
5930 if (c == '/') {
5931 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005932 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005933 repl = idx + 1;
5934 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005935 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005936 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005937 *idx++ = c;
5938 if (!inquotes && c == '\\' && arg[1] == '\\')
5939 arg++; /* skip both \\, not just first one */
5940 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005941 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005942 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005943
5944 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005945}
5946#endif /* ENABLE_ASH_BASH_COMPAT */
5947
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005948static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005949subevalvar(char *p, char *str, int strloc, int subtype,
5950 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005951{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005952 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005953 char *startp;
5954 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005955 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005956 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5957 USE_ASH_BASH_COMPAT(char null = '\0';)
5958 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5959 int saveherefd = herefd;
5960 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005961 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005962 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005963
5964 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005965 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5966 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005967 STPUTC('\0', expdest);
5968 herefd = saveherefd;
5969 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005970 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005971
5972 switch (subtype) {
5973 case VSASSIGN:
5974 setvar(str, startp, 0);
5975 amount = startp - expdest;
5976 STADJUST(amount, expdest);
5977 return startp;
5978
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005979#if ENABLE_ASH_BASH_COMPAT
5980 case VSSUBSTR:
5981 loc = str = stackblock() + strloc;
5982// TODO: number() instead? It does error checking...
5983 pos = atoi(loc);
5984 len = str - startp - 1;
5985
5986 /* *loc != '\0', guaranteed by parser */
5987 if (quotes) {
5988 char *ptr;
5989
5990 /* We must adjust the length by the number of escapes we find. */
5991 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00005992 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005993 len--;
5994 ptr++;
5995 }
5996 }
5997 }
5998 orig_len = len;
5999
6000 if (*loc++ == ':') {
6001// TODO: number() instead? It does error checking...
6002 len = atoi(loc);
6003 } else {
6004 len = orig_len;
6005 while (*loc && *loc != ':')
6006 loc++;
6007 if (*loc++ == ':')
6008// TODO: number() instead? It does error checking...
6009 len = atoi(loc);
6010 }
6011 if (pos >= orig_len) {
6012 pos = 0;
6013 len = 0;
6014 }
6015 if (len > (orig_len - pos))
6016 len = orig_len - pos;
6017
6018 for (str = startp; pos; str++, pos--) {
6019 if (quotes && *str == CTLESC)
6020 str++;
6021 }
6022 for (loc = startp; len; len--) {
6023 if (quotes && *str == CTLESC)
6024 *loc++ = *str++;
6025 *loc++ = *str++;
6026 }
6027 *loc = '\0';
6028 amount = loc - expdest;
6029 STADJUST(amount, expdest);
6030 return loc;
6031#endif
6032
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006033 case VSQUESTION:
6034 varunset(p, str, startp, varflags);
6035 /* NOTREACHED */
6036 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006037 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006038
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006039 /* We'll comeback here if we grow the stack while handling
6040 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6041 * stack will need rebasing, and we'll need to remove our work
6042 * areas each time
6043 */
6044 USE_ASH_BASH_COMPAT(restart:)
6045
6046 amount = expdest - ((char *)stackblock() + resetloc);
6047 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006048 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006049
6050 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006051 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006052 if (quotes) {
6053 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6054 if (rmesc != startp) {
6055 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006056 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006057 }
6058 }
6059 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006060 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006061 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006062 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006063
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006064#if ENABLE_ASH_BASH_COMPAT
6065 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6066 char *idx, *end, *restart_detect;
6067
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006068 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006069 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6070 if (!repl)
6071 repl = &null;
6072 }
6073
6074 /* If there's no pattern to match, return the expansion unmolested */
6075 if (*str == '\0')
6076 return 0;
6077
6078 len = 0;
6079 idx = startp;
6080 end = str - 1;
6081 while (idx < end) {
6082 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6083 if (!loc) {
6084 /* No match, advance */
6085 restart_detect = stackblock();
6086 STPUTC(*idx, expdest);
6087 if (quotes && *idx == CTLESC) {
6088 idx++;
6089 len++;
6090 STPUTC(*idx, expdest);
6091 }
6092 if (stackblock() != restart_detect)
6093 goto restart;
6094 idx++;
6095 len++;
6096 rmesc++;
6097 continue;
6098 }
6099
6100 if (subtype == VSREPLACEALL) {
6101 while (idx < loc) {
6102 if (quotes && *idx == CTLESC)
6103 idx++;
6104 idx++;
6105 rmesc++;
6106 }
6107 } else
6108 idx = loc;
6109
6110 for (loc = repl; *loc; loc++) {
6111 restart_detect = stackblock();
6112 STPUTC(*loc, expdest);
6113 if (stackblock() != restart_detect)
6114 goto restart;
6115 len++;
6116 }
6117
6118 if (subtype == VSREPLACE) {
6119 while (*idx) {
6120 restart_detect = stackblock();
6121 STPUTC(*idx, expdest);
6122 if (stackblock() != restart_detect)
6123 goto restart;
6124 len++;
6125 idx++;
6126 }
6127 break;
6128 }
6129 }
6130
6131 /* We've put the replaced text into a buffer at workloc, now
6132 * move it to the right place and adjust the stack.
6133 */
6134 startp = stackblock() + startloc;
6135 STPUTC('\0', expdest);
6136 memmove(startp, stackblock() + workloc, len);
6137 startp[len++] = '\0';
6138 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6139 STADJUST(-amount, expdest);
6140 return startp;
6141 }
6142#endif /* ENABLE_ASH_BASH_COMPAT */
6143
6144 subtype -= VSTRIMRIGHT;
6145#if DEBUG
6146 if (subtype < 0 || subtype > 7)
6147 abort();
6148#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006149 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6150 zero = subtype >> 1;
6151 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6152 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6153
6154 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6155 if (loc) {
6156 if (zero) {
6157 memmove(startp, loc, str - loc);
6158 loc = startp + (str - loc) - 1;
6159 }
6160 *loc = '\0';
6161 amount = loc - expdest;
6162 STADJUST(amount, expdest);
6163 }
6164 return loc;
6165}
6166
6167/*
6168 * Add the value of a specialized variable to the stack string.
6169 */
6170static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006171varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006172{
6173 int num;
6174 char *p;
6175 int i;
6176 int sep = 0;
6177 int sepq = 0;
6178 ssize_t len = 0;
6179 char **ap;
6180 int syntax;
6181 int quoted = varflags & VSQUOTE;
6182 int subtype = varflags & VSTYPE;
6183 int quotes = flags & (EXP_FULL | EXP_CASE);
6184
6185 if (quoted && (flags & EXP_FULL))
6186 sep = 1 << CHAR_BIT;
6187
6188 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6189 switch (*name) {
6190 case '$':
6191 num = rootpid;
6192 goto numvar;
6193 case '?':
6194 num = exitstatus;
6195 goto numvar;
6196 case '#':
6197 num = shellparam.nparam;
6198 goto numvar;
6199 case '!':
6200 num = backgndpid;
6201 if (num == 0)
6202 return -1;
6203 numvar:
6204 len = cvtnum(num);
6205 break;
6206 case '-':
6207 p = makestrspace(NOPTS, expdest);
6208 for (i = NOPTS - 1; i >= 0; i--) {
6209 if (optlist[i]) {
6210 USTPUTC(optletters(i), p);
6211 len++;
6212 }
6213 }
6214 expdest = p;
6215 break;
6216 case '@':
6217 if (sep)
6218 goto param;
6219 /* fall through */
6220 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006221 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6223 sepq = 1;
6224 param:
6225 ap = shellparam.p;
6226 if (!ap)
6227 return -1;
6228 while ((p = *ap++)) {
6229 size_t partlen;
6230
6231 partlen = strlen(p);
6232 len += partlen;
6233
6234 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6235 memtodest(p, partlen, syntax, quotes);
6236
6237 if (*ap && sep) {
6238 char *q;
6239
6240 len++;
6241 if (subtype == VSPLUS || subtype == VSLENGTH) {
6242 continue;
6243 }
6244 q = expdest;
6245 if (sepq)
6246 STPUTC(CTLESC, q);
6247 STPUTC(sep, q);
6248 expdest = q;
6249 }
6250 }
6251 return len;
6252 case '0':
6253 case '1':
6254 case '2':
6255 case '3':
6256 case '4':
6257 case '5':
6258 case '6':
6259 case '7':
6260 case '8':
6261 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006262// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006263 num = atoi(name);
6264 if (num < 0 || num > shellparam.nparam)
6265 return -1;
6266 p = num ? shellparam.p[num - 1] : arg0;
6267 goto value;
6268 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006269 /* NB: name has form "VAR=..." */
6270
6271 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6272 * which should be considered before we check variables. */
6273 if (var_str_list) {
6274 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6275 p = NULL;
6276 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006277 char *str, *eq;
6278 str = var_str_list->text;
6279 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006280 if (!eq) /* stop at first non-assignment */
6281 break;
6282 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006283 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006284 && strncmp(str, name, name_len) == 0) {
6285 p = eq;
6286 /* goto value; - WRONG! */
6287 /* think "A=1 A=2 B=$A" */
6288 }
6289 var_str_list = var_str_list->next;
6290 } while (var_str_list);
6291 if (p)
6292 goto value;
6293 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006294 p = lookupvar(name);
6295 value:
6296 if (!p)
6297 return -1;
6298
6299 len = strlen(p);
6300 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6301 memtodest(p, len, syntax, quotes);
6302 return len;
6303 }
6304
6305 if (subtype == VSPLUS || subtype == VSLENGTH)
6306 STADJUST(-len, expdest);
6307 return len;
6308}
6309
6310/*
6311 * Expand a variable, and return a pointer to the next character in the
6312 * input string.
6313 */
6314static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006315evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006316{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006317 char varflags;
6318 char subtype;
6319 char quoted;
6320 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006321 char *var;
6322 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006323 int startloc;
6324 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006325
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326 varflags = *p++;
6327 subtype = varflags & VSTYPE;
6328 quoted = varflags & VSQUOTE;
6329 var = p;
6330 easy = (!quoted || (*var == '@' && shellparam.nparam));
6331 startloc = expdest - (char *)stackblock();
6332 p = strchr(p, '=') + 1;
6333
6334 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006335 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336 if (varflags & VSNUL)
6337 varlen--;
6338
6339 if (subtype == VSPLUS) {
6340 varlen = -1 - varlen;
6341 goto vsplus;
6342 }
6343
6344 if (subtype == VSMINUS) {
6345 vsplus:
6346 if (varlen < 0) {
6347 argstr(
6348 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006349 (quoted ? EXP_QWORD : EXP_WORD),
6350 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006351 );
6352 goto end;
6353 }
6354 if (easy)
6355 goto record;
6356 goto end;
6357 }
6358
6359 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6360 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006361 if (subevalvar(p, var, /* strloc: */ 0,
6362 subtype, startloc, varflags,
6363 /* quotes: */ 0,
6364 var_str_list)
6365 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006366 varflags &= ~VSNUL;
6367 /*
6368 * Remove any recorded regions beyond
6369 * start of variable
6370 */
6371 removerecordregions(startloc);
6372 goto again;
6373 }
6374 goto end;
6375 }
6376 if (easy)
6377 goto record;
6378 goto end;
6379 }
6380
6381 if (varlen < 0 && uflag)
6382 varunset(p, var, 0, 0);
6383
6384 if (subtype == VSLENGTH) {
6385 cvtnum(varlen > 0 ? varlen : 0);
6386 goto record;
6387 }
6388
6389 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006390 if (easy)
6391 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006392 goto end;
6393 }
6394
6395#if DEBUG
6396 switch (subtype) {
6397 case VSTRIMLEFT:
6398 case VSTRIMLEFTMAX:
6399 case VSTRIMRIGHT:
6400 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006401#if ENABLE_ASH_BASH_COMPAT
6402 case VSSUBSTR:
6403 case VSREPLACE:
6404 case VSREPLACEALL:
6405#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006406 break;
6407 default:
6408 abort();
6409 }
6410#endif
6411
6412 if (varlen >= 0) {
6413 /*
6414 * Terminate the string and start recording the pattern
6415 * right after it
6416 */
6417 STPUTC('\0', expdest);
6418 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006419 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6420 startloc, varflags,
6421 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6422 var_str_list)
6423 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006424 int amount = expdest - (
6425 (char *)stackblock() + patloc - 1
6426 );
6427 STADJUST(-amount, expdest);
6428 }
6429 /* Remove any recorded regions beyond start of variable */
6430 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006431 record:
6432 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006433 }
6434
6435 end:
6436 if (subtype != VSNORMAL) { /* skip to end of alternative */
6437 int nesting = 1;
6438 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006439 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006440 if (c == CTLESC)
6441 p++;
6442 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6443 if (varlen >= 0)
6444 argbackq = argbackq->next;
6445 } else if (c == CTLVAR) {
6446 if ((*p++ & VSTYPE) != VSNORMAL)
6447 nesting++;
6448 } else if (c == CTLENDVAR) {
6449 if (--nesting == 0)
6450 break;
6451 }
6452 }
6453 }
6454 return p;
6455}
6456
6457/*
6458 * Break the argument string into pieces based upon IFS and add the
6459 * strings to the argument list. The regions of the string to be
6460 * searched for IFS characters have been stored by recordregion.
6461 */
6462static void
6463ifsbreakup(char *string, struct arglist *arglist)
6464{
6465 struct ifsregion *ifsp;
6466 struct strlist *sp;
6467 char *start;
6468 char *p;
6469 char *q;
6470 const char *ifs, *realifs;
6471 int ifsspc;
6472 int nulonly;
6473
6474 start = string;
6475 if (ifslastp != NULL) {
6476 ifsspc = 0;
6477 nulonly = 0;
6478 realifs = ifsset() ? ifsval() : defifs;
6479 ifsp = &ifsfirst;
6480 do {
6481 p = string + ifsp->begoff;
6482 nulonly = ifsp->nulonly;
6483 ifs = nulonly ? nullstr : realifs;
6484 ifsspc = 0;
6485 while (p < string + ifsp->endoff) {
6486 q = p;
6487 if (*p == CTLESC)
6488 p++;
6489 if (!strchr(ifs, *p)) {
6490 p++;
6491 continue;
6492 }
6493 if (!nulonly)
6494 ifsspc = (strchr(defifs, *p) != NULL);
6495 /* Ignore IFS whitespace at start */
6496 if (q == start && ifsspc) {
6497 p++;
6498 start = p;
6499 continue;
6500 }
6501 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006502 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006503 sp->text = start;
6504 *arglist->lastp = sp;
6505 arglist->lastp = &sp->next;
6506 p++;
6507 if (!nulonly) {
6508 for (;;) {
6509 if (p >= string + ifsp->endoff) {
6510 break;
6511 }
6512 q = p;
6513 if (*p == CTLESC)
6514 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006515 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006516 p = q;
6517 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006518 }
6519 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006520 if (ifsspc) {
6521 p++;
6522 ifsspc = 0;
6523 } else {
6524 p = q;
6525 break;
6526 }
6527 } else
6528 p++;
6529 }
6530 }
6531 start = p;
6532 } /* while */
6533 ifsp = ifsp->next;
6534 } while (ifsp != NULL);
6535 if (nulonly)
6536 goto add;
6537 }
6538
6539 if (!*start)
6540 return;
6541
6542 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006543 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544 sp->text = start;
6545 *arglist->lastp = sp;
6546 arglist->lastp = &sp->next;
6547}
6548
6549static void
6550ifsfree(void)
6551{
6552 struct ifsregion *p;
6553
6554 INT_OFF;
6555 p = ifsfirst.next;
6556 do {
6557 struct ifsregion *ifsp;
6558 ifsp = p->next;
6559 free(p);
6560 p = ifsp;
6561 } while (p);
6562 ifslastp = NULL;
6563 ifsfirst.next = NULL;
6564 INT_ON;
6565}
6566
6567/*
6568 * Add a file name to the list.
6569 */
6570static void
6571addfname(const char *name)
6572{
6573 struct strlist *sp;
6574
Denis Vlasenko597906c2008-02-20 16:38:54 +00006575 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006576 sp->text = ststrdup(name);
6577 *exparg.lastp = sp;
6578 exparg.lastp = &sp->next;
6579}
6580
6581static char *expdir;
6582
6583/*
6584 * Do metacharacter (i.e. *, ?, [...]) expansion.
6585 */
6586static void
6587expmeta(char *enddir, char *name)
6588{
6589 char *p;
6590 const char *cp;
6591 char *start;
6592 char *endname;
6593 int metaflag;
6594 struct stat statb;
6595 DIR *dirp;
6596 struct dirent *dp;
6597 int atend;
6598 int matchdot;
6599
6600 metaflag = 0;
6601 start = name;
6602 for (p = name; *p; p++) {
6603 if (*p == '*' || *p == '?')
6604 metaflag = 1;
6605 else if (*p == '[') {
6606 char *q = p + 1;
6607 if (*q == '!')
6608 q++;
6609 for (;;) {
6610 if (*q == '\\')
6611 q++;
6612 if (*q == '/' || *q == '\0')
6613 break;
6614 if (*++q == ']') {
6615 metaflag = 1;
6616 break;
6617 }
6618 }
6619 } else if (*p == '\\')
6620 p++;
6621 else if (*p == '/') {
6622 if (metaflag)
6623 goto out;
6624 start = p + 1;
6625 }
6626 }
6627 out:
6628 if (metaflag == 0) { /* we've reached the end of the file name */
6629 if (enddir != expdir)
6630 metaflag++;
6631 p = name;
6632 do {
6633 if (*p == '\\')
6634 p++;
6635 *enddir++ = *p;
6636 } while (*p++);
6637 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6638 addfname(expdir);
6639 return;
6640 }
6641 endname = p;
6642 if (name < start) {
6643 p = name;
6644 do {
6645 if (*p == '\\')
6646 p++;
6647 *enddir++ = *p++;
6648 } while (p < start);
6649 }
6650 if (enddir == expdir) {
6651 cp = ".";
6652 } else if (enddir == expdir + 1 && *expdir == '/') {
6653 cp = "/";
6654 } else {
6655 cp = expdir;
6656 enddir[-1] = '\0';
6657 }
6658 dirp = opendir(cp);
6659 if (dirp == NULL)
6660 return;
6661 if (enddir != expdir)
6662 enddir[-1] = '/';
6663 if (*endname == 0) {
6664 atend = 1;
6665 } else {
6666 atend = 0;
6667 *endname++ = '\0';
6668 }
6669 matchdot = 0;
6670 p = start;
6671 if (*p == '\\')
6672 p++;
6673 if (*p == '.')
6674 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006675 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006676 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006677 continue;
6678 if (pmatch(start, dp->d_name)) {
6679 if (atend) {
6680 strcpy(enddir, dp->d_name);
6681 addfname(expdir);
6682 } else {
6683 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6684 continue;
6685 p[-1] = '/';
6686 expmeta(p, endname);
6687 }
6688 }
6689 }
6690 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006691 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006692 endname[-1] = '/';
6693}
6694
6695static struct strlist *
6696msort(struct strlist *list, int len)
6697{
6698 struct strlist *p, *q = NULL;
6699 struct strlist **lpp;
6700 int half;
6701 int n;
6702
6703 if (len <= 1)
6704 return list;
6705 half = len >> 1;
6706 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006707 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006708 q = p;
6709 p = p->next;
6710 }
6711 q->next = NULL; /* terminate first half of list */
6712 q = msort(list, half); /* sort first half of list */
6713 p = msort(p, len - half); /* sort second half */
6714 lpp = &list;
6715 for (;;) {
6716#if ENABLE_LOCALE_SUPPORT
6717 if (strcoll(p->text, q->text) < 0)
6718#else
6719 if (strcmp(p->text, q->text) < 0)
6720#endif
6721 {
6722 *lpp = p;
6723 lpp = &p->next;
6724 p = *lpp;
6725 if (p == NULL) {
6726 *lpp = q;
6727 break;
6728 }
6729 } else {
6730 *lpp = q;
6731 lpp = &q->next;
6732 q = *lpp;
6733 if (q == NULL) {
6734 *lpp = p;
6735 break;
6736 }
6737 }
6738 }
6739 return list;
6740}
6741
6742/*
6743 * Sort the results of file name expansion. It calculates the number of
6744 * strings to sort and then calls msort (short for merge sort) to do the
6745 * work.
6746 */
6747static struct strlist *
6748expsort(struct strlist *str)
6749{
6750 int len;
6751 struct strlist *sp;
6752
6753 len = 0;
6754 for (sp = str; sp; sp = sp->next)
6755 len++;
6756 return msort(str, len);
6757}
6758
6759static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006760expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006761{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006762 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006763 '*', '?', '[', 0
6764 };
6765 /* TODO - EXP_REDIR */
6766
6767 while (str) {
6768 struct strlist **savelastp;
6769 struct strlist *sp;
6770 char *p;
6771
6772 if (fflag)
6773 goto nometa;
6774 if (!strpbrk(str->text, metachars))
6775 goto nometa;
6776 savelastp = exparg.lastp;
6777
6778 INT_OFF;
6779 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6780 {
6781 int i = strlen(str->text);
6782 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6783 }
6784
6785 expmeta(expdir, p);
6786 free(expdir);
6787 if (p != str->text)
6788 free(p);
6789 INT_ON;
6790 if (exparg.lastp == savelastp) {
6791 /*
6792 * no matches
6793 */
6794 nometa:
6795 *exparg.lastp = str;
6796 rmescapes(str->text);
6797 exparg.lastp = &str->next;
6798 } else {
6799 *exparg.lastp = NULL;
6800 *savelastp = sp = expsort(*savelastp);
6801 while (sp->next != NULL)
6802 sp = sp->next;
6803 exparg.lastp = &sp->next;
6804 }
6805 str = str->next;
6806 }
6807}
6808
6809/*
6810 * Perform variable substitution and command substitution on an argument,
6811 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6812 * perform splitting and file name expansion. When arglist is NULL, perform
6813 * here document expansion.
6814 */
6815static void
6816expandarg(union node *arg, struct arglist *arglist, int flag)
6817{
6818 struct strlist *sp;
6819 char *p;
6820
6821 argbackq = arg->narg.backquote;
6822 STARTSTACKSTR(expdest);
6823 ifsfirst.next = NULL;
6824 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006825 argstr(arg->narg.text, flag,
6826 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006827 p = _STPUTC('\0', expdest);
6828 expdest = p - 1;
6829 if (arglist == NULL) {
6830 return; /* here document expanded */
6831 }
6832 p = grabstackstr(p);
6833 exparg.lastp = &exparg.list;
6834 /*
6835 * TODO - EXP_REDIR
6836 */
6837 if (flag & EXP_FULL) {
6838 ifsbreakup(p, &exparg);
6839 *exparg.lastp = NULL;
6840 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006841 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006842 } else {
6843 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6844 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006845 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006846 sp->text = p;
6847 *exparg.lastp = sp;
6848 exparg.lastp = &sp->next;
6849 }
6850 if (ifsfirst.next)
6851 ifsfree();
6852 *exparg.lastp = NULL;
6853 if (exparg.list) {
6854 *arglist->lastp = exparg.list;
6855 arglist->lastp = exparg.lastp;
6856 }
6857}
6858
6859/*
6860 * Expand shell variables and backquotes inside a here document.
6861 */
6862static void
6863expandhere(union node *arg, int fd)
6864{
6865 herefd = fd;
6866 expandarg(arg, (struct arglist *)NULL, 0);
6867 full_write(fd, stackblock(), expdest - (char *)stackblock());
6868}
6869
6870/*
6871 * Returns true if the pattern matches the string.
6872 */
6873static int
6874patmatch(char *pattern, const char *string)
6875{
6876 return pmatch(preglob(pattern, 0, 0), string);
6877}
6878
6879/*
6880 * See if a pattern matches in a case statement.
6881 */
6882static int
6883casematch(union node *pattern, char *val)
6884{
6885 struct stackmark smark;
6886 int result;
6887
6888 setstackmark(&smark);
6889 argbackq = pattern->narg.backquote;
6890 STARTSTACKSTR(expdest);
6891 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006892 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6893 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006894 STACKSTRNUL(expdest);
6895 result = patmatch(stackblock(), val);
6896 popstackmark(&smark);
6897 return result;
6898}
6899
6900
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006901/* ============ find_command */
6902
6903struct builtincmd {
6904 const char *name;
6905 int (*builtin)(int, char **);
6906 /* unsigned flags; */
6907};
6908#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006909/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006910 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006911#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006912#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006913
6914struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006915 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006916 union param {
6917 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006918 /* index >= 0 for commands without path (slashes) */
6919 /* (TODO: what exactly does the value mean? PATH position?) */
6920 /* index == -1 for commands with slashes */
6921 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006922 const struct builtincmd *cmd;
6923 struct funcnode *func;
6924 } u;
6925};
6926/* values of cmdtype */
6927#define CMDUNKNOWN -1 /* no entry in table for command */
6928#define CMDNORMAL 0 /* command is an executable program */
6929#define CMDFUNCTION 1 /* command is a shell function */
6930#define CMDBUILTIN 2 /* command is a shell builtin */
6931
6932/* action to find_command() */
6933#define DO_ERR 0x01 /* prints errors */
6934#define DO_ABS 0x02 /* checks absolute paths */
6935#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6936#define DO_ALTPATH 0x08 /* using alternate path */
6937#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6938
6939static void find_command(char *, struct cmdentry *, int, const char *);
6940
6941
6942/* ============ Hashing commands */
6943
6944/*
6945 * When commands are first encountered, they are entered in a hash table.
6946 * This ensures that a full path search will not have to be done for them
6947 * on each invocation.
6948 *
6949 * We should investigate converting to a linear search, even though that
6950 * would make the command name "hash" a misnomer.
6951 */
6952
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006953struct tblentry {
6954 struct tblentry *next; /* next entry in hash chain */
6955 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006956 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006957 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006958 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006959};
6960
Denis Vlasenko01631112007-12-16 17:20:38 +00006961static struct tblentry **cmdtable;
6962#define INIT_G_cmdtable() do { \
6963 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6964} while (0)
6965
6966static int builtinloc = -1; /* index in path of %builtin, or -1 */
6967
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006968
6969static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006970tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006971{
6972 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006973
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006974#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006975 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00006976 if (APPLET_IS_NOEXEC(applet_no)) {
6977 while (*envp)
6978 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006979 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00006980 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006981 /* re-exec ourselves with the new arguments */
6982 execve(bb_busybox_exec_path, argv, envp);
6983 /* If they called chroot or otherwise made the binary no longer
6984 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006985 }
6986#endif
6987
6988 repeat:
6989#ifdef SYSV
6990 do {
6991 execve(cmd, argv, envp);
6992 } while (errno == EINTR);
6993#else
6994 execve(cmd, argv, envp);
6995#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006996 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006997 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006998 return;
6999 }
7000 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007001 char **ap;
7002 char **new;
7003
7004 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007005 continue;
7006 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007007 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007008 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007009 ap += 2;
7010 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007011 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007012 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007013 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007014 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007015 goto repeat;
7016 }
7017}
7018
7019/*
7020 * Exec a program. Never returns. If you change this routine, you may
7021 * have to change the find_command routine as well.
7022 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007023static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007024static void
7025shellexec(char **argv, const char *path, int idx)
7026{
7027 char *cmdname;
7028 int e;
7029 char **envp;
7030 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007031#if ENABLE_FEATURE_SH_STANDALONE
7032 int applet_no = -1;
7033#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007034
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007035 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007036 envp = listvars(VEXPORT, VUNSET, 0);
7037 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007038#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007039 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007040#endif
7041 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007042 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007043 e = errno;
7044 } else {
7045 e = ENOENT;
7046 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7047 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007048 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007049 if (errno != ENOENT && errno != ENOTDIR)
7050 e = errno;
7051 }
7052 stunalloc(cmdname);
7053 }
7054 }
7055
7056 /* Map to POSIX errors */
7057 switch (e) {
7058 case EACCES:
7059 exerrno = 126;
7060 break;
7061 case ENOENT:
7062 exerrno = 127;
7063 break;
7064 default:
7065 exerrno = 2;
7066 break;
7067 }
7068 exitstatus = exerrno;
7069 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007070 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007071 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7072 /* NOTREACHED */
7073}
7074
7075static void
7076printentry(struct tblentry *cmdp)
7077{
7078 int idx;
7079 const char *path;
7080 char *name;
7081
7082 idx = cmdp->param.index;
7083 path = pathval();
7084 do {
7085 name = padvance(&path, cmdp->cmdname);
7086 stunalloc(name);
7087 } while (--idx >= 0);
7088 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7089}
7090
7091/*
7092 * Clear out command entries. The argument specifies the first entry in
7093 * PATH which has changed.
7094 */
7095static void
7096clearcmdentry(int firstchange)
7097{
7098 struct tblentry **tblp;
7099 struct tblentry **pp;
7100 struct tblentry *cmdp;
7101
7102 INT_OFF;
7103 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7104 pp = tblp;
7105 while ((cmdp = *pp) != NULL) {
7106 if ((cmdp->cmdtype == CMDNORMAL &&
7107 cmdp->param.index >= firstchange)
7108 || (cmdp->cmdtype == CMDBUILTIN &&
7109 builtinloc >= firstchange)
7110 ) {
7111 *pp = cmdp->next;
7112 free(cmdp);
7113 } else {
7114 pp = &cmdp->next;
7115 }
7116 }
7117 }
7118 INT_ON;
7119}
7120
7121/*
7122 * Locate a command in the command hash table. If "add" is nonzero,
7123 * add the command to the table if it is not already present. The
7124 * variable "lastcmdentry" is set to point to the address of the link
7125 * pointing to the entry, so that delete_cmd_entry can delete the
7126 * entry.
7127 *
7128 * Interrupts must be off if called with add != 0.
7129 */
7130static struct tblentry **lastcmdentry;
7131
7132static struct tblentry *
7133cmdlookup(const char *name, int add)
7134{
7135 unsigned int hashval;
7136 const char *p;
7137 struct tblentry *cmdp;
7138 struct tblentry **pp;
7139
7140 p = name;
7141 hashval = (unsigned char)*p << 4;
7142 while (*p)
7143 hashval += (unsigned char)*p++;
7144 hashval &= 0x7FFF;
7145 pp = &cmdtable[hashval % CMDTABLESIZE];
7146 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7147 if (strcmp(cmdp->cmdname, name) == 0)
7148 break;
7149 pp = &cmdp->next;
7150 }
7151 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007152 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7153 + strlen(name)
7154 /* + 1 - already done because
7155 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007156 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007157 cmdp->cmdtype = CMDUNKNOWN;
7158 strcpy(cmdp->cmdname, name);
7159 }
7160 lastcmdentry = pp;
7161 return cmdp;
7162}
7163
7164/*
7165 * Delete the command entry returned on the last lookup.
7166 */
7167static void
7168delete_cmd_entry(void)
7169{
7170 struct tblentry *cmdp;
7171
7172 INT_OFF;
7173 cmdp = *lastcmdentry;
7174 *lastcmdentry = cmdp->next;
7175 if (cmdp->cmdtype == CMDFUNCTION)
7176 freefunc(cmdp->param.func);
7177 free(cmdp);
7178 INT_ON;
7179}
7180
7181/*
7182 * Add a new command entry, replacing any existing command entry for
7183 * the same name - except special builtins.
7184 */
7185static void
7186addcmdentry(char *name, struct cmdentry *entry)
7187{
7188 struct tblentry *cmdp;
7189
7190 cmdp = cmdlookup(name, 1);
7191 if (cmdp->cmdtype == CMDFUNCTION) {
7192 freefunc(cmdp->param.func);
7193 }
7194 cmdp->cmdtype = entry->cmdtype;
7195 cmdp->param = entry->u;
7196 cmdp->rehash = 0;
7197}
7198
7199static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007200hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007201{
7202 struct tblentry **pp;
7203 struct tblentry *cmdp;
7204 int c;
7205 struct cmdentry entry;
7206 char *name;
7207
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007208 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007209 clearcmdentry(0);
7210 return 0;
7211 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007212
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007213 if (*argptr == NULL) {
7214 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7215 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7216 if (cmdp->cmdtype == CMDNORMAL)
7217 printentry(cmdp);
7218 }
7219 }
7220 return 0;
7221 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007222
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007223 c = 0;
7224 while ((name = *argptr) != NULL) {
7225 cmdp = cmdlookup(name, 0);
7226 if (cmdp != NULL
7227 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007228 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7229 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007230 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007231 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007232 find_command(name, &entry, DO_ERR, pathval());
7233 if (entry.cmdtype == CMDUNKNOWN)
7234 c = 1;
7235 argptr++;
7236 }
7237 return c;
7238}
7239
7240/*
7241 * Called when a cd is done. Marks all commands so the next time they
7242 * are executed they will be rehashed.
7243 */
7244static void
7245hashcd(void)
7246{
7247 struct tblentry **pp;
7248 struct tblentry *cmdp;
7249
7250 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7251 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007252 if (cmdp->cmdtype == CMDNORMAL
7253 || (cmdp->cmdtype == CMDBUILTIN
7254 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7255 && builtinloc > 0)
7256 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007257 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007258 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007259 }
7260 }
7261}
7262
7263/*
7264 * Fix command hash table when PATH changed.
7265 * Called before PATH is changed. The argument is the new value of PATH;
7266 * pathval() still returns the old value at this point.
7267 * Called with interrupts off.
7268 */
7269static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007270changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007271{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007272 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007273 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007274 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007275 int idx_bltin;
7276
7277 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007278 firstchange = 9999; /* assume no change */
7279 idx = 0;
7280 idx_bltin = -1;
7281 for (;;) {
7282 if (*old != *new) {
7283 firstchange = idx;
7284 if ((*old == '\0' && *new == ':')
7285 || (*old == ':' && *new == '\0'))
7286 firstchange++;
7287 old = new; /* ignore subsequent differences */
7288 }
7289 if (*new == '\0')
7290 break;
7291 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7292 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007293 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007294 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007295 new++, old++;
7296 }
7297 if (builtinloc < 0 && idx_bltin >= 0)
7298 builtinloc = idx_bltin; /* zap builtins */
7299 if (builtinloc >= 0 && idx_bltin < 0)
7300 firstchange = 0;
7301 clearcmdentry(firstchange);
7302 builtinloc = idx_bltin;
7303}
7304
7305#define TEOF 0
7306#define TNL 1
7307#define TREDIR 2
7308#define TWORD 3
7309#define TSEMI 4
7310#define TBACKGND 5
7311#define TAND 6
7312#define TOR 7
7313#define TPIPE 8
7314#define TLP 9
7315#define TRP 10
7316#define TENDCASE 11
7317#define TENDBQUOTE 12
7318#define TNOT 13
7319#define TCASE 14
7320#define TDO 15
7321#define TDONE 16
7322#define TELIF 17
7323#define TELSE 18
7324#define TESAC 19
7325#define TFI 20
7326#define TFOR 21
7327#define TIF 22
7328#define TIN 23
7329#define TTHEN 24
7330#define TUNTIL 25
7331#define TWHILE 26
7332#define TBEGIN 27
7333#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007334typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007335
7336/* first char is indicating which tokens mark the end of a list */
7337static const char *const tokname_array[] = {
7338 "\1end of file",
7339 "\0newline",
7340 "\0redirection",
7341 "\0word",
7342 "\0;",
7343 "\0&",
7344 "\0&&",
7345 "\0||",
7346 "\0|",
7347 "\0(",
7348 "\1)",
7349 "\1;;",
7350 "\1`",
7351#define KWDOFFSET 13
7352 /* the following are keywords */
7353 "\0!",
7354 "\0case",
7355 "\1do",
7356 "\1done",
7357 "\1elif",
7358 "\1else",
7359 "\1esac",
7360 "\1fi",
7361 "\0for",
7362 "\0if",
7363 "\0in",
7364 "\1then",
7365 "\0until",
7366 "\0while",
7367 "\0{",
7368 "\1}",
7369};
7370
7371static const char *
7372tokname(int tok)
7373{
7374 static char buf[16];
7375
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007376//try this:
7377//if (tok < TSEMI) return tokname_array[tok] + 1;
7378//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7379//return buf;
7380
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007381 if (tok >= TSEMI)
7382 buf[0] = '"';
7383 sprintf(buf + (tok >= TSEMI), "%s%c",
7384 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7385 return buf;
7386}
7387
7388/* Wrapper around strcmp for qsort/bsearch/... */
7389static int
7390pstrcmp(const void *a, const void *b)
7391{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007392 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007393}
7394
7395static const char *const *
7396findkwd(const char *s)
7397{
7398 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007399 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7400 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401}
7402
7403/*
7404 * Locate and print what a word is...
7405 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007406static int
7407describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007408{
7409 struct cmdentry entry;
7410 struct tblentry *cmdp;
7411#if ENABLE_ASH_ALIAS
7412 const struct alias *ap;
7413#endif
7414 const char *path = pathval();
7415
7416 if (describe_command_verbose) {
7417 out1str(command);
7418 }
7419
7420 /* First look at the keywords */
7421 if (findkwd(command)) {
7422 out1str(describe_command_verbose ? " is a shell keyword" : command);
7423 goto out;
7424 }
7425
7426#if ENABLE_ASH_ALIAS
7427 /* Then look at the aliases */
7428 ap = lookupalias(command, 0);
7429 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007430 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007431 out1str("alias ");
7432 printalias(ap);
7433 return 0;
7434 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007435 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007436 goto out;
7437 }
7438#endif
7439 /* Then check if it is a tracked alias */
7440 cmdp = cmdlookup(command, 0);
7441 if (cmdp != NULL) {
7442 entry.cmdtype = cmdp->cmdtype;
7443 entry.u = cmdp->param;
7444 } else {
7445 /* Finally use brute force */
7446 find_command(command, &entry, DO_ABS, path);
7447 }
7448
7449 switch (entry.cmdtype) {
7450 case CMDNORMAL: {
7451 int j = entry.u.index;
7452 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007453 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007454 p = command;
7455 } else {
7456 do {
7457 p = padvance(&path, command);
7458 stunalloc(p);
7459 } while (--j >= 0);
7460 }
7461 if (describe_command_verbose) {
7462 out1fmt(" is%s %s",
7463 (cmdp ? " a tracked alias for" : nullstr), p
7464 );
7465 } else {
7466 out1str(p);
7467 }
7468 break;
7469 }
7470
7471 case CMDFUNCTION:
7472 if (describe_command_verbose) {
7473 out1str(" is a shell function");
7474 } else {
7475 out1str(command);
7476 }
7477 break;
7478
7479 case CMDBUILTIN:
7480 if (describe_command_verbose) {
7481 out1fmt(" is a %sshell builtin",
7482 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7483 "special " : nullstr
7484 );
7485 } else {
7486 out1str(command);
7487 }
7488 break;
7489
7490 default:
7491 if (describe_command_verbose) {
7492 out1str(": not found\n");
7493 }
7494 return 127;
7495 }
7496 out:
7497 outstr("\n", stdout);
7498 return 0;
7499}
7500
7501static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007502typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007503{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007504 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007505 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007506 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007507
Denis Vlasenko46846e22007-05-20 13:08:31 +00007508 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007509 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007510 i++;
7511 verbose = 0;
7512 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007513 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007514 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007515 }
7516 return err;
7517}
7518
7519#if ENABLE_ASH_CMDCMD
7520static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007521commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007522{
7523 int c;
7524 enum {
7525 VERIFY_BRIEF = 1,
7526 VERIFY_VERBOSE = 2,
7527 } verify = 0;
7528
7529 while ((c = nextopt("pvV")) != '\0')
7530 if (c == 'V')
7531 verify |= VERIFY_VERBOSE;
7532 else if (c == 'v')
7533 verify |= VERIFY_BRIEF;
7534#if DEBUG
7535 else if (c != 'p')
7536 abort();
7537#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007538 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7539 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007540 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007541 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007542
7543 return 0;
7544}
7545#endif
7546
7547
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007548/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007549
Denis Vlasenko340299a2008-11-21 10:36:36 +00007550static int funcblocksize; /* size of structures in function */
7551static int funcstringsize; /* size of strings in node */
7552static void *funcblock; /* block to allocate function from */
7553static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007554
Eric Andersencb57d552001-06-28 07:25:16 +00007555/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007556#define EV_EXIT 01 /* exit after evaluating tree */
7557#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007558#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007559
Denis Vlasenko340299a2008-11-21 10:36:36 +00007560static const short nodesize[N_NUMBER] = {
7561 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7562 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7563 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7564 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7565 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7566 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7567 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7568 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7569 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7570 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7571 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7572 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7573 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7574 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7575 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7576 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7577 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007578#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007579 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007580#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007581 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7582 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7583 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7584 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7585 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7586 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7587 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7588 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7589 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007590};
7591
7592static void calcsize(union node *n);
7593
7594static void
7595sizenodelist(struct nodelist *lp)
7596{
7597 while (lp) {
7598 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7599 calcsize(lp->n);
7600 lp = lp->next;
7601 }
7602}
7603
7604static void
7605calcsize(union node *n)
7606{
7607 if (n == NULL)
7608 return;
7609 funcblocksize += nodesize[n->type];
7610 switch (n->type) {
7611 case NCMD:
7612 calcsize(n->ncmd.redirect);
7613 calcsize(n->ncmd.args);
7614 calcsize(n->ncmd.assign);
7615 break;
7616 case NPIPE:
7617 sizenodelist(n->npipe.cmdlist);
7618 break;
7619 case NREDIR:
7620 case NBACKGND:
7621 case NSUBSHELL:
7622 calcsize(n->nredir.redirect);
7623 calcsize(n->nredir.n);
7624 break;
7625 case NAND:
7626 case NOR:
7627 case NSEMI:
7628 case NWHILE:
7629 case NUNTIL:
7630 calcsize(n->nbinary.ch2);
7631 calcsize(n->nbinary.ch1);
7632 break;
7633 case NIF:
7634 calcsize(n->nif.elsepart);
7635 calcsize(n->nif.ifpart);
7636 calcsize(n->nif.test);
7637 break;
7638 case NFOR:
7639 funcstringsize += strlen(n->nfor.var) + 1;
7640 calcsize(n->nfor.body);
7641 calcsize(n->nfor.args);
7642 break;
7643 case NCASE:
7644 calcsize(n->ncase.cases);
7645 calcsize(n->ncase.expr);
7646 break;
7647 case NCLIST:
7648 calcsize(n->nclist.body);
7649 calcsize(n->nclist.pattern);
7650 calcsize(n->nclist.next);
7651 break;
7652 case NDEFUN:
7653 case NARG:
7654 sizenodelist(n->narg.backquote);
7655 funcstringsize += strlen(n->narg.text) + 1;
7656 calcsize(n->narg.next);
7657 break;
7658 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007659#if ENABLE_ASH_BASH_COMPAT
7660 case NTO2:
7661#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662 case NCLOBBER:
7663 case NFROM:
7664 case NFROMTO:
7665 case NAPPEND:
7666 calcsize(n->nfile.fname);
7667 calcsize(n->nfile.next);
7668 break;
7669 case NTOFD:
7670 case NFROMFD:
7671 calcsize(n->ndup.vname);
7672 calcsize(n->ndup.next);
7673 break;
7674 case NHERE:
7675 case NXHERE:
7676 calcsize(n->nhere.doc);
7677 calcsize(n->nhere.next);
7678 break;
7679 case NNOT:
7680 calcsize(n->nnot.com);
7681 break;
7682 };
7683}
7684
7685static char *
7686nodeckstrdup(char *s)
7687{
7688 char *rtn = funcstring;
7689
7690 strcpy(funcstring, s);
7691 funcstring += strlen(s) + 1;
7692 return rtn;
7693}
7694
7695static union node *copynode(union node *);
7696
7697static struct nodelist *
7698copynodelist(struct nodelist *lp)
7699{
7700 struct nodelist *start;
7701 struct nodelist **lpp;
7702
7703 lpp = &start;
7704 while (lp) {
7705 *lpp = funcblock;
7706 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7707 (*lpp)->n = copynode(lp->n);
7708 lp = lp->next;
7709 lpp = &(*lpp)->next;
7710 }
7711 *lpp = NULL;
7712 return start;
7713}
7714
7715static union node *
7716copynode(union node *n)
7717{
7718 union node *new;
7719
7720 if (n == NULL)
7721 return NULL;
7722 new = funcblock;
7723 funcblock = (char *) funcblock + nodesize[n->type];
7724
7725 switch (n->type) {
7726 case NCMD:
7727 new->ncmd.redirect = copynode(n->ncmd.redirect);
7728 new->ncmd.args = copynode(n->ncmd.args);
7729 new->ncmd.assign = copynode(n->ncmd.assign);
7730 break;
7731 case NPIPE:
7732 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007733 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007734 break;
7735 case NREDIR:
7736 case NBACKGND:
7737 case NSUBSHELL:
7738 new->nredir.redirect = copynode(n->nredir.redirect);
7739 new->nredir.n = copynode(n->nredir.n);
7740 break;
7741 case NAND:
7742 case NOR:
7743 case NSEMI:
7744 case NWHILE:
7745 case NUNTIL:
7746 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7747 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7748 break;
7749 case NIF:
7750 new->nif.elsepart = copynode(n->nif.elsepart);
7751 new->nif.ifpart = copynode(n->nif.ifpart);
7752 new->nif.test = copynode(n->nif.test);
7753 break;
7754 case NFOR:
7755 new->nfor.var = nodeckstrdup(n->nfor.var);
7756 new->nfor.body = copynode(n->nfor.body);
7757 new->nfor.args = copynode(n->nfor.args);
7758 break;
7759 case NCASE:
7760 new->ncase.cases = copynode(n->ncase.cases);
7761 new->ncase.expr = copynode(n->ncase.expr);
7762 break;
7763 case NCLIST:
7764 new->nclist.body = copynode(n->nclist.body);
7765 new->nclist.pattern = copynode(n->nclist.pattern);
7766 new->nclist.next = copynode(n->nclist.next);
7767 break;
7768 case NDEFUN:
7769 case NARG:
7770 new->narg.backquote = copynodelist(n->narg.backquote);
7771 new->narg.text = nodeckstrdup(n->narg.text);
7772 new->narg.next = copynode(n->narg.next);
7773 break;
7774 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007775#if ENABLE_ASH_BASH_COMPAT
7776 case NTO2:
7777#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007778 case NCLOBBER:
7779 case NFROM:
7780 case NFROMTO:
7781 case NAPPEND:
7782 new->nfile.fname = copynode(n->nfile.fname);
7783 new->nfile.fd = n->nfile.fd;
7784 new->nfile.next = copynode(n->nfile.next);
7785 break;
7786 case NTOFD:
7787 case NFROMFD:
7788 new->ndup.vname = copynode(n->ndup.vname);
7789 new->ndup.dupfd = n->ndup.dupfd;
7790 new->ndup.fd = n->ndup.fd;
7791 new->ndup.next = copynode(n->ndup.next);
7792 break;
7793 case NHERE:
7794 case NXHERE:
7795 new->nhere.doc = copynode(n->nhere.doc);
7796 new->nhere.fd = n->nhere.fd;
7797 new->nhere.next = copynode(n->nhere.next);
7798 break;
7799 case NNOT:
7800 new->nnot.com = copynode(n->nnot.com);
7801 break;
7802 };
7803 new->type = n->type;
7804 return new;
7805}
7806
7807/*
7808 * Make a copy of a parse tree.
7809 */
7810static struct funcnode *
7811copyfunc(union node *n)
7812{
7813 struct funcnode *f;
7814 size_t blocksize;
7815
7816 funcblocksize = offsetof(struct funcnode, n);
7817 funcstringsize = 0;
7818 calcsize(n);
7819 blocksize = funcblocksize;
7820 f = ckmalloc(blocksize + funcstringsize);
7821 funcblock = (char *) f + offsetof(struct funcnode, n);
7822 funcstring = (char *) f + blocksize;
7823 copynode(n);
7824 f->count = 0;
7825 return f;
7826}
7827
7828/*
7829 * Define a shell function.
7830 */
7831static void
7832defun(char *name, union node *func)
7833{
7834 struct cmdentry entry;
7835
7836 INT_OFF;
7837 entry.cmdtype = CMDFUNCTION;
7838 entry.u.func = copyfunc(func);
7839 addcmdentry(name, &entry);
7840 INT_ON;
7841}
7842
7843static int evalskip; /* set if we are skipping commands */
7844/* reasons for skipping commands (see comment on breakcmd routine) */
7845#define SKIPBREAK (1 << 0)
7846#define SKIPCONT (1 << 1)
7847#define SKIPFUNC (1 << 2)
7848#define SKIPFILE (1 << 3)
7849#define SKIPEVAL (1 << 4)
7850static int skipcount; /* number of levels to skip */
7851static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007852static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007853
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007854/* forward decl way out to parsing code - dotrap needs it */
7855static int evalstring(char *s, int mask);
7856
7857/*
7858 * Called to execute a trap. Perhaps we should avoid entering new trap
7859 * handlers while we are executing a trap handler.
7860 */
7861static int
7862dotrap(void)
7863{
7864 char *p;
7865 char *q;
7866 int i;
7867 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007868 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007869
7870 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007871 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007872 xbarrier();
7873
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007874 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007875 if (!*q)
7876 continue;
7877 *q = '\0';
7878
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007879 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007880 if (!p)
7881 continue;
7882 skip = evalstring(p, SKIPEVAL);
7883 exitstatus = savestatus;
7884 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007885 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007886 }
7887
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007888 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007889}
7890
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007891/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007892static void evalloop(union node *, int);
7893static void evalfor(union node *, int);
7894static void evalcase(union node *, int);
7895static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007896static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007897static void evalpipe(union node *, int);
7898static void evalcommand(union node *, int);
7899static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007900static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007901
Eric Andersen62483552001-07-10 06:09:16 +00007902/*
Eric Andersenc470f442003-07-28 09:56:35 +00007903 * Evaluate a parse tree. The value is left in the global variable
7904 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007905 */
Eric Andersenc470f442003-07-28 09:56:35 +00007906static void
7907evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007908{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007909
7910 struct jmploc *volatile savehandler = exception_handler;
7911 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007912 int checkexit = 0;
7913 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007914 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007915
Eric Andersenc470f442003-07-28 09:56:35 +00007916 if (n == NULL) {
7917 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007918 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007919 }
Eric Andersenc470f442003-07-28 09:56:35 +00007920 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007921 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007922
7923 exception_handler = &jmploc;
7924 {
7925 int err = setjmp(jmploc.loc);
7926 if (err) {
7927 /* if it was a signal, check for trap handlers */
7928 if (exception == EXSIG)
7929 goto out;
7930 /* continue on the way out */
7931 exception_handler = savehandler;
7932 longjmp(exception_handler->loc, err);
7933 }
7934 }
7935
Eric Andersenc470f442003-07-28 09:56:35 +00007936 switch (n->type) {
7937 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007938#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007939 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007940 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007941 break;
7942#endif
7943 case NNOT:
7944 evaltree(n->nnot.com, EV_TESTED);
7945 status = !exitstatus;
7946 goto setstatus;
7947 case NREDIR:
7948 expredir(n->nredir.redirect);
7949 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7950 if (!status) {
7951 evaltree(n->nredir.n, flags & EV_TESTED);
7952 status = exitstatus;
7953 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007954 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00007955 goto setstatus;
7956 case NCMD:
7957 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007958 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007959 if (eflag && !(flags & EV_TESTED))
7960 checkexit = ~0;
7961 goto calleval;
7962 case NFOR:
7963 evalfn = evalfor;
7964 goto calleval;
7965 case NWHILE:
7966 case NUNTIL:
7967 evalfn = evalloop;
7968 goto calleval;
7969 case NSUBSHELL:
7970 case NBACKGND:
7971 evalfn = evalsubshell;
7972 goto calleval;
7973 case NPIPE:
7974 evalfn = evalpipe;
7975 goto checkexit;
7976 case NCASE:
7977 evalfn = evalcase;
7978 goto calleval;
7979 case NAND:
7980 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007981 case NSEMI: {
7982
Eric Andersenc470f442003-07-28 09:56:35 +00007983#if NAND + 1 != NOR
7984#error NAND + 1 != NOR
7985#endif
7986#if NOR + 1 != NSEMI
7987#error NOR + 1 != NSEMI
7988#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00007989 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00007990 evaltree(
7991 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007992 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00007993 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007994 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00007995 break;
7996 if (!evalskip) {
7997 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007998 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007999 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008000 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008001 evalfn(n, flags);
8002 break;
8003 }
8004 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008005 }
Eric Andersenc470f442003-07-28 09:56:35 +00008006 case NIF:
8007 evaltree(n->nif.test, EV_TESTED);
8008 if (evalskip)
8009 break;
8010 if (exitstatus == 0) {
8011 n = n->nif.ifpart;
8012 goto evaln;
8013 } else if (n->nif.elsepart) {
8014 n = n->nif.elsepart;
8015 goto evaln;
8016 }
8017 goto success;
8018 case NDEFUN:
8019 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008020 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008021 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008022 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008023 exitstatus = status;
8024 break;
8025 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008026
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008027 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008028 exception_handler = savehandler;
8029 out1:
8030 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008031 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008032 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008033 goto exexit;
8034
8035 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008036 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008037 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008038 }
Eric Andersen62483552001-07-10 06:09:16 +00008039}
8040
Eric Andersenc470f442003-07-28 09:56:35 +00008041#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8042static
8043#endif
8044void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8045
Eric Andersenc470f442003-07-28 09:56:35 +00008046static void
8047evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008048{
8049 int status;
8050
8051 loopnest++;
8052 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008053 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008054 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008055 int i;
8056
Eric Andersencb57d552001-06-28 07:25:16 +00008057 evaltree(n->nbinary.ch1, EV_TESTED);
8058 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008059 skipping:
8060 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008061 evalskip = 0;
8062 continue;
8063 }
8064 if (evalskip == SKIPBREAK && --skipcount <= 0)
8065 evalskip = 0;
8066 break;
8067 }
Eric Andersenc470f442003-07-28 09:56:35 +00008068 i = exitstatus;
8069 if (n->type != NWHILE)
8070 i = !i;
8071 if (i != 0)
8072 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008073 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008074 status = exitstatus;
8075 if (evalskip)
8076 goto skipping;
8077 }
8078 loopnest--;
8079 exitstatus = status;
8080}
8081
Eric Andersenc470f442003-07-28 09:56:35 +00008082static void
8083evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008084{
8085 struct arglist arglist;
8086 union node *argp;
8087 struct strlist *sp;
8088 struct stackmark smark;
8089
8090 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008091 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008092 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008093 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008094 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008095 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008096 if (evalskip)
8097 goto out;
8098 }
8099 *arglist.lastp = NULL;
8100
8101 exitstatus = 0;
8102 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008103 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008104 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008105 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008106 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008107 if (evalskip) {
8108 if (evalskip == SKIPCONT && --skipcount <= 0) {
8109 evalskip = 0;
8110 continue;
8111 }
8112 if (evalskip == SKIPBREAK && --skipcount <= 0)
8113 evalskip = 0;
8114 break;
8115 }
8116 }
8117 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008118 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008119 popstackmark(&smark);
8120}
8121
Eric Andersenc470f442003-07-28 09:56:35 +00008122static void
8123evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008124{
8125 union node *cp;
8126 union node *patp;
8127 struct arglist arglist;
8128 struct stackmark smark;
8129
8130 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008131 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008132 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008133 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008134 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008135 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8136 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008137 if (casematch(patp, arglist.list->text)) {
8138 if (evalskip == 0) {
8139 evaltree(cp->nclist.body, flags);
8140 }
8141 goto out;
8142 }
8143 }
8144 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008145 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008146 popstackmark(&smark);
8147}
8148
Eric Andersenc470f442003-07-28 09:56:35 +00008149/*
8150 * Kick off a subshell to evaluate a tree.
8151 */
Eric Andersenc470f442003-07-28 09:56:35 +00008152static void
8153evalsubshell(union node *n, int flags)
8154{
8155 struct job *jp;
8156 int backgnd = (n->type == NBACKGND);
8157 int status;
8158
8159 expredir(n->nredir.redirect);
8160 if (!backgnd && flags & EV_EXIT && !trap[0])
8161 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008162 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008163 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008164 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008165 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008166 flags |= EV_EXIT;
8167 if (backgnd)
8168 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008169 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008170 redirect(n->nredir.redirect, 0);
8171 evaltreenr(n->nredir.n, flags);
8172 /* never returns */
8173 }
8174 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008175 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008176 status = waitforjob(jp);
8177 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008178 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008179}
8180
Eric Andersenc470f442003-07-28 09:56:35 +00008181/*
8182 * Compute the names of the files in a redirection list.
8183 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008184static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008185static void
8186expredir(union node *n)
8187{
8188 union node *redir;
8189
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008190 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008191 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008192
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008193 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008194 fn.lastp = &fn.list;
8195 switch (redir->type) {
8196 case NFROMTO:
8197 case NFROM:
8198 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008199#if ENABLE_ASH_BASH_COMPAT
8200 case NTO2:
8201#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008202 case NCLOBBER:
8203 case NAPPEND:
8204 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008205#if ENABLE_ASH_BASH_COMPAT
8206 store_expfname:
8207#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008208 redir->nfile.expfname = fn.list->text;
8209 break;
8210 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008211 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008212 if (redir->ndup.vname) {
8213 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008214 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008215 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008216#if ENABLE_ASH_BASH_COMPAT
8217//FIXME: we used expandarg with different args!
8218 if (!isdigit_str9(fn.list->text)) {
8219 /* >&file, not >&fd */
8220 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8221 ash_msg_and_raise_error("redir error");
8222 redir->type = NTO2;
8223 goto store_expfname;
8224 }
8225#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008226 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008227 }
8228 break;
8229 }
8230 }
8231}
8232
Eric Andersencb57d552001-06-28 07:25:16 +00008233/*
Eric Andersencb57d552001-06-28 07:25:16 +00008234 * Evaluate a pipeline. All the processes in the pipeline are children
8235 * of the process creating the pipeline. (This differs from some versions
8236 * of the shell, which make the last process in a pipeline the parent
8237 * of all the rest.)
8238 */
Eric Andersenc470f442003-07-28 09:56:35 +00008239static void
8240evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008241{
8242 struct job *jp;
8243 struct nodelist *lp;
8244 int pipelen;
8245 int prevfd;
8246 int pip[2];
8247
Eric Andersenc470f442003-07-28 09:56:35 +00008248 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008249 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008250 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008251 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008252 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008253 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008254 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008255 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008256 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008257 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008258 pip[1] = -1;
8259 if (lp->next) {
8260 if (pipe(pip) < 0) {
8261 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008262 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008263 }
8264 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008265 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008266 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008267 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008268 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008269 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008270 if (prevfd > 0) {
8271 dup2(prevfd, 0);
8272 close(prevfd);
8273 }
8274 if (pip[1] > 1) {
8275 dup2(pip[1], 1);
8276 close(pip[1]);
8277 }
Eric Andersenc470f442003-07-28 09:56:35 +00008278 evaltreenr(lp->n, flags);
8279 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008280 }
8281 if (prevfd >= 0)
8282 close(prevfd);
8283 prevfd = pip[0];
8284 close(pip[1]);
8285 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008286 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008287 exitstatus = waitforjob(jp);
8288 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008289 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008290 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008291}
8292
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008293/*
8294 * Controls whether the shell is interactive or not.
8295 */
8296static void
8297setinteractive(int on)
8298{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008299 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008300
8301 if (++on == is_interactive)
8302 return;
8303 is_interactive = on;
8304 setsignal(SIGINT);
8305 setsignal(SIGQUIT);
8306 setsignal(SIGTERM);
8307#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8308 if (is_interactive > 1) {
8309 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008310 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008311
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008312 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008313 out1fmt(
8314 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008315 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008316 "Enter 'help' for a list of built-in commands."
8317 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008318 bb_banner);
8319 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008320 }
8321 }
8322#endif
8323}
8324
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008325static void
8326optschanged(void)
8327{
8328#if DEBUG
8329 opentrace();
8330#endif
8331 setinteractive(iflag);
8332 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008333#if ENABLE_FEATURE_EDITING_VI
8334 if (viflag)
8335 line_input_state->flags |= VI_MODE;
8336 else
8337 line_input_state->flags &= ~VI_MODE;
8338#else
8339 viflag = 0; /* forcibly keep the option off */
8340#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008341}
8342
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008343static struct localvar *localvars;
8344
8345/*
8346 * Called after a function returns.
8347 * Interrupts must be off.
8348 */
8349static void
8350poplocalvars(void)
8351{
8352 struct localvar *lvp;
8353 struct var *vp;
8354
8355 while ((lvp = localvars) != NULL) {
8356 localvars = lvp->next;
8357 vp = lvp->vp;
8358 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8359 if (vp == NULL) { /* $- saved */
8360 memcpy(optlist, lvp->text, sizeof(optlist));
8361 free((char*)lvp->text);
8362 optschanged();
8363 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8364 unsetvar(vp->text);
8365 } else {
8366 if (vp->func)
8367 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8368 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8369 free((char*)vp->text);
8370 vp->flags = lvp->flags;
8371 vp->text = lvp->text;
8372 }
8373 free(lvp);
8374 }
8375}
8376
8377static int
8378evalfun(struct funcnode *func, int argc, char **argv, int flags)
8379{
8380 volatile struct shparam saveparam;
8381 struct localvar *volatile savelocalvars;
8382 struct jmploc *volatile savehandler;
8383 struct jmploc jmploc;
8384 int e;
8385
8386 saveparam = shellparam;
8387 savelocalvars = localvars;
8388 e = setjmp(jmploc.loc);
8389 if (e) {
8390 goto funcdone;
8391 }
8392 INT_OFF;
8393 savehandler = exception_handler;
8394 exception_handler = &jmploc;
8395 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008396 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008397 func->count++;
8398 funcnest++;
8399 INT_ON;
8400 shellparam.nparam = argc - 1;
8401 shellparam.p = argv + 1;
8402#if ENABLE_ASH_GETOPTS
8403 shellparam.optind = 1;
8404 shellparam.optoff = -1;
8405#endif
8406 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008407 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008408 INT_OFF;
8409 funcnest--;
8410 freefunc(func);
8411 poplocalvars();
8412 localvars = savelocalvars;
8413 freeparam(&shellparam);
8414 shellparam = saveparam;
8415 exception_handler = savehandler;
8416 INT_ON;
8417 evalskip &= ~SKIPFUNC;
8418 return e;
8419}
8420
Denis Vlasenko131ae172007-02-18 13:00:19 +00008421#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008422static char **
8423parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008424{
8425 char *cp, c;
8426
8427 for (;;) {
8428 cp = *++argv;
8429 if (!cp)
8430 return 0;
8431 if (*cp++ != '-')
8432 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008433 c = *cp++;
8434 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008435 break;
8436 if (c == '-' && !*cp) {
8437 argv++;
8438 break;
8439 }
8440 do {
8441 switch (c) {
8442 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008443 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008444 break;
8445 default:
8446 /* run 'typecmd' for other options */
8447 return 0;
8448 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008449 c = *cp++;
8450 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008451 }
8452 return argv;
8453}
8454#endif
8455
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008456/*
8457 * Make a variable a local variable. When a variable is made local, it's
8458 * value and flags are saved in a localvar structure. The saved values
8459 * will be restored when the shell function returns. We handle the name
8460 * "-" as a special case.
8461 */
8462static void
8463mklocal(char *name)
8464{
8465 struct localvar *lvp;
8466 struct var **vpp;
8467 struct var *vp;
8468
8469 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008470 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008471 if (LONE_DASH(name)) {
8472 char *p;
8473 p = ckmalloc(sizeof(optlist));
8474 lvp->text = memcpy(p, optlist, sizeof(optlist));
8475 vp = NULL;
8476 } else {
8477 char *eq;
8478
8479 vpp = hashvar(name);
8480 vp = *findvar(vpp, name);
8481 eq = strchr(name, '=');
8482 if (vp == NULL) {
8483 if (eq)
8484 setvareq(name, VSTRFIXED);
8485 else
8486 setvar(name, NULL, VSTRFIXED);
8487 vp = *vpp; /* the new variable */
8488 lvp->flags = VUNSET;
8489 } else {
8490 lvp->text = vp->text;
8491 lvp->flags = vp->flags;
8492 vp->flags |= VSTRFIXED|VTEXTFIXED;
8493 if (eq)
8494 setvareq(name, 0);
8495 }
8496 }
8497 lvp->vp = vp;
8498 lvp->next = localvars;
8499 localvars = lvp;
8500 INT_ON;
8501}
8502
8503/*
8504 * The "local" command.
8505 */
8506static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008507localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008508{
8509 char *name;
8510
8511 argv = argptr;
8512 while ((name = *argv++) != NULL) {
8513 mklocal(name);
8514 }
8515 return 0;
8516}
8517
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008518static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008519falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008520{
8521 return 1;
8522}
8523
8524static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008525truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008526{
8527 return 0;
8528}
8529
8530static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008531execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008532{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008533 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008534 iflag = 0; /* exit on error */
8535 mflag = 0;
8536 optschanged();
8537 shellexec(argv + 1, pathval(), 0);
8538 }
8539 return 0;
8540}
8541
8542/*
8543 * The return command.
8544 */
8545static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008546returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008547{
8548 /*
8549 * If called outside a function, do what ksh does;
8550 * skip the rest of the file.
8551 */
8552 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8553 return argv[1] ? number(argv[1]) : exitstatus;
8554}
8555
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008556/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008557static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008558static int dotcmd(int, char **);
8559static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008560static int exitcmd(int, char **);
8561static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008562#if ENABLE_ASH_GETOPTS
8563static int getoptscmd(int, char **);
8564#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008565#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008566static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008567#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008568#if ENABLE_ASH_MATH_SUPPORT
8569static int letcmd(int, char **);
8570#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008571static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008572static int setcmd(int, char **);
8573static int shiftcmd(int, char **);
8574static int timescmd(int, char **);
8575static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008576static int umaskcmd(int, char **);
8577static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008578static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008579
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008580#define BUILTIN_NOSPEC "0"
8581#define BUILTIN_SPECIAL "1"
8582#define BUILTIN_REGULAR "2"
8583#define BUILTIN_SPEC_REG "3"
8584#define BUILTIN_ASSIGN "4"
8585#define BUILTIN_SPEC_ASSG "5"
8586#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008587#define BUILTIN_SPEC_REG_ASSG "7"
8588
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008589/* We do not handle [[ expr ]] bashism bash-compatibly,
8590 * we make it a synonym of [ expr ].
8591 * Basically, word splitting and pathname expansion should NOT be performed
8592 * Examples:
8593 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8594 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8595 * Additional operators:
8596 * || and && should work as -o and -a
8597 * =~ regexp match
8598 * Apart from the above, [[ expr ]] should work as [ expr ]
8599 */
8600
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008601#define echocmd echo_main
8602#define printfcmd printf_main
8603#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008604
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008605/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008606static const struct builtincmd builtintab[] = {
8607 { BUILTIN_SPEC_REG ".", dotcmd },
8608 { BUILTIN_SPEC_REG ":", truecmd },
8609#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008610 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008611#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008612 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008613#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008614#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008615#if ENABLE_ASH_ALIAS
8616 { BUILTIN_REG_ASSG "alias", aliascmd },
8617#endif
8618#if JOBS
8619 { BUILTIN_REGULAR "bg", fg_bgcmd },
8620#endif
8621 { BUILTIN_SPEC_REG "break", breakcmd },
8622 { BUILTIN_REGULAR "cd", cdcmd },
8623 { BUILTIN_NOSPEC "chdir", cdcmd },
8624#if ENABLE_ASH_CMDCMD
8625 { BUILTIN_REGULAR "command", commandcmd },
8626#endif
8627 { BUILTIN_SPEC_REG "continue", breakcmd },
8628#if ENABLE_ASH_BUILTIN_ECHO
8629 { BUILTIN_REGULAR "echo", echocmd },
8630#endif
8631 { BUILTIN_SPEC_REG "eval", evalcmd },
8632 { BUILTIN_SPEC_REG "exec", execcmd },
8633 { BUILTIN_SPEC_REG "exit", exitcmd },
8634 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8635 { BUILTIN_REGULAR "false", falsecmd },
8636#if JOBS
8637 { BUILTIN_REGULAR "fg", fg_bgcmd },
8638#endif
8639#if ENABLE_ASH_GETOPTS
8640 { BUILTIN_REGULAR "getopts", getoptscmd },
8641#endif
8642 { BUILTIN_NOSPEC "hash", hashcmd },
8643#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8644 { BUILTIN_NOSPEC "help", helpcmd },
8645#endif
8646#if JOBS
8647 { BUILTIN_REGULAR "jobs", jobscmd },
8648 { BUILTIN_REGULAR "kill", killcmd },
8649#endif
8650#if ENABLE_ASH_MATH_SUPPORT
8651 { BUILTIN_NOSPEC "let", letcmd },
8652#endif
8653 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008654#if ENABLE_ASH_BUILTIN_PRINTF
8655 { BUILTIN_REGULAR "printf", printfcmd },
8656#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008657 { BUILTIN_NOSPEC "pwd", pwdcmd },
8658 { BUILTIN_REGULAR "read", readcmd },
8659 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8660 { BUILTIN_SPEC_REG "return", returncmd },
8661 { BUILTIN_SPEC_REG "set", setcmd },
8662 { BUILTIN_SPEC_REG "shift", shiftcmd },
8663 { BUILTIN_SPEC_REG "source", dotcmd },
8664#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008665 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008666#endif
8667 { BUILTIN_SPEC_REG "times", timescmd },
8668 { BUILTIN_SPEC_REG "trap", trapcmd },
8669 { BUILTIN_REGULAR "true", truecmd },
8670 { BUILTIN_NOSPEC "type", typecmd },
8671 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8672 { BUILTIN_REGULAR "umask", umaskcmd },
8673#if ENABLE_ASH_ALIAS
8674 { BUILTIN_REGULAR "unalias", unaliascmd },
8675#endif
8676 { BUILTIN_SPEC_REG "unset", unsetcmd },
8677 { BUILTIN_REGULAR "wait", waitcmd },
8678};
8679
Denis Vlasenko80591b02008-03-25 07:49:43 +00008680/* Should match the above table! */
8681#define COMMANDCMD (builtintab + \
8682 2 + \
8683 1 * ENABLE_ASH_BUILTIN_TEST + \
8684 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8685 1 * ENABLE_ASH_ALIAS + \
8686 1 * ENABLE_ASH_JOB_CONTROL + \
8687 3)
8688#define EXECCMD (builtintab + \
8689 2 + \
8690 1 * ENABLE_ASH_BUILTIN_TEST + \
8691 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8692 1 * ENABLE_ASH_ALIAS + \
8693 1 * ENABLE_ASH_JOB_CONTROL + \
8694 3 + \
8695 1 * ENABLE_ASH_CMDCMD + \
8696 1 + \
8697 ENABLE_ASH_BUILTIN_ECHO + \
8698 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008699
8700/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008701 * Search the table of builtin commands.
8702 */
8703static struct builtincmd *
8704find_builtin(const char *name)
8705{
8706 struct builtincmd *bp;
8707
8708 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008709 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008710 pstrcmp
8711 );
8712 return bp;
8713}
8714
8715/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008716 * Execute a simple command.
8717 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008718static int
8719isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008720{
8721 const char *q = endofname(p);
8722 if (p == q)
8723 return 0;
8724 return *q == '=';
8725}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008726static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008727bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008728{
8729 /* Preserve exitstatus of a previous possible redirection
8730 * as POSIX mandates */
8731 return back_exitstatus;
8732}
Eric Andersenc470f442003-07-28 09:56:35 +00008733static void
8734evalcommand(union node *cmd, int flags)
8735{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008736 static const struct builtincmd null_bltin = {
8737 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008738 };
Eric Andersenc470f442003-07-28 09:56:35 +00008739 struct stackmark smark;
8740 union node *argp;
8741 struct arglist arglist;
8742 struct arglist varlist;
8743 char **argv;
8744 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008745 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008746 struct cmdentry cmdentry;
8747 struct job *jp;
8748 char *lastarg;
8749 const char *path;
8750 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008751 int status;
8752 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008753 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008754 smallint cmd_is_exec;
8755 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008756
8757 /* First expand the arguments. */
8758 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8759 setstackmark(&smark);
8760 back_exitstatus = 0;
8761
8762 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008763 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008764 varlist.lastp = &varlist.list;
8765 *varlist.lastp = NULL;
8766 arglist.lastp = &arglist.list;
8767 *arglist.lastp = NULL;
8768
8769 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008770 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008771 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8772 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8773 }
8774
Eric Andersenc470f442003-07-28 09:56:35 +00008775 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8776 struct strlist **spp;
8777
8778 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008779 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008780 expandarg(argp, &arglist, EXP_VARTILDE);
8781 else
8782 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8783
Eric Andersenc470f442003-07-28 09:56:35 +00008784 for (sp = *spp; sp; sp = sp->next)
8785 argc++;
8786 }
8787
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008788 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008789 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008790 TRACE(("evalcommand arg: %s\n", sp->text));
8791 *nargv++ = sp->text;
8792 }
8793 *nargv = NULL;
8794
8795 lastarg = NULL;
8796 if (iflag && funcnest == 0 && argc > 0)
8797 lastarg = nargv[-1];
8798
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008799 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008800 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008801 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008802
8803 path = vpath.text;
8804 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8805 struct strlist **spp;
8806 char *p;
8807
8808 spp = varlist.lastp;
8809 expandarg(argp, &varlist, EXP_VARTILDE);
8810
8811 /*
8812 * Modify the command lookup path, if a PATH= assignment
8813 * is present
8814 */
8815 p = (*spp)->text;
8816 if (varequal(p, path))
8817 path = p;
8818 }
8819
8820 /* Print the command if xflag is set. */
8821 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008822 int n;
8823 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008824
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008825 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008826 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008827
8828 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008829 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008830 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008831 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008832 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008833 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008834 p--;
8835 }
8836 }
8837 sp = arglist.list;
8838 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008839 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008840 }
8841
8842 cmd_is_exec = 0;
8843 spclbltin = -1;
8844
8845 /* Now locate the command. */
8846 if (argc) {
8847 const char *oldpath;
8848 int cmd_flag = DO_ERR;
8849
8850 path += 5;
8851 oldpath = path;
8852 for (;;) {
8853 find_command(argv[0], &cmdentry, cmd_flag, path);
8854 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008855 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008856 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008857 goto bail;
8858 }
8859
8860 /* implement bltin and command here */
8861 if (cmdentry.cmdtype != CMDBUILTIN)
8862 break;
8863 if (spclbltin < 0)
8864 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8865 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008866 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008867#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008868 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008869 path = oldpath;
8870 nargv = parse_command_args(argv, &path);
8871 if (!nargv)
8872 break;
8873 argc -= nargv - argv;
8874 argv = nargv;
8875 cmd_flag |= DO_NOFUNC;
8876 } else
8877#endif
8878 break;
8879 }
8880 }
8881
8882 if (status) {
8883 /* We have a redirection error. */
8884 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008885 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008886 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008887 exitstatus = status;
8888 goto out;
8889 }
8890
8891 /* Execute the command. */
8892 switch (cmdentry.cmdtype) {
8893 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008894
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008895#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008896/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
8897 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008898 {
8899 /* find_command() encodes applet_no as (-2 - applet_no) */
8900 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008901 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008902 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008903 /* run <applet>_main() */
8904 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008905 break;
8906 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008907 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008908#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008909 /* Fork off a child process if necessary. */
8910 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008911 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008912 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008913 if (forkshell(jp, cmd, FORK_FG) != 0) {
8914 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008915 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008916 break;
8917 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008918 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008919 }
8920 listsetvar(varlist.list, VEXPORT|VSTACK);
8921 shellexec(argv, path, cmdentry.u.index);
8922 /* NOTREACHED */
8923
8924 case CMDBUILTIN:
8925 cmdenviron = varlist.list;
8926 if (cmdenviron) {
8927 struct strlist *list = cmdenviron;
8928 int i = VNOSET;
8929 if (spclbltin > 0 || argc == 0) {
8930 i = 0;
8931 if (cmd_is_exec && argc > 1)
8932 i = VEXPORT;
8933 }
8934 listsetvar(list, i);
8935 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008936 /* Tight loop with builtins only:
8937 * "while kill -0 $child; do true; done"
8938 * will never exit even if $child died, unless we do this
8939 * to reap the zombie and make kill detect that it's gone: */
8940 dowait(DOWAIT_NONBLOCK, NULL);
8941
Eric Andersenc470f442003-07-28 09:56:35 +00008942 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8943 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008944 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008945 if (i == EXEXIT)
8946 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008947 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008948 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008949 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008950 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008951 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008952 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008953 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008954 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008955 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008956 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008957 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008958 }
8959 break;
8960
8961 case CMDFUNCTION:
8962 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00008963 /* See above for the rationale */
8964 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00008965 if (evalfun(cmdentry.u.func, argc, argv, flags))
8966 goto raise;
8967 break;
8968 }
8969
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008970 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008971 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008972 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008973 /* dsl: I think this is intended to be used to support
8974 * '_' in 'vi' command mode during line editing...
8975 * However I implemented that within libedit itself.
8976 */
8977 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008978 }
Eric Andersenc470f442003-07-28 09:56:35 +00008979 popstackmark(&smark);
8980}
8981
8982static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008983evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8984{
Eric Andersenc470f442003-07-28 09:56:35 +00008985 char *volatile savecmdname;
8986 struct jmploc *volatile savehandler;
8987 struct jmploc jmploc;
8988 int i;
8989
8990 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008991 i = setjmp(jmploc.loc);
8992 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008993 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008994 savehandler = exception_handler;
8995 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008996 commandname = argv[0];
8997 argptr = argv + 1;
8998 optptr = NULL; /* initialize nextopt */
8999 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009000 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009001 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009002 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009003 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009004 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009005// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009006 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009007
9008 return i;
9009}
9010
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009011static int
9012goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009013{
9014 return !*endofname(p);
9015}
9016
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009017
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009018/*
9019 * Search for a command. This is called before we fork so that the
9020 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009021 * the child. The check for "goodname" is an overly conservative
9022 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009023 */
Eric Andersenc470f442003-07-28 09:56:35 +00009024static void
9025prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009026{
9027 struct cmdentry entry;
9028
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009029 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9030 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009031}
9032
Eric Andersencb57d552001-06-28 07:25:16 +00009033
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009034/* ============ Builtin commands
9035 *
9036 * Builtin commands whose functions are closely tied to evaluation
9037 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009038 */
9039
9040/*
Eric Andersencb57d552001-06-28 07:25:16 +00009041 * Handle break and continue commands. Break, continue, and return are
9042 * all handled by setting the evalskip flag. The evaluation routines
9043 * above all check this flag, and if it is set they start skipping
9044 * commands rather than executing them. The variable skipcount is
9045 * the number of loops to break/continue, or the number of function
9046 * levels to return. (The latter is always 1.) It should probably
9047 * be an error to break out of more loops than exist, but it isn't
9048 * in the standard shell so we don't make it one here.
9049 */
Eric Andersenc470f442003-07-28 09:56:35 +00009050static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009051breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009052{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009053 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009054
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009055 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009056 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009057 if (n > loopnest)
9058 n = loopnest;
9059 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009060 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009061 skipcount = n;
9062 }
9063 return 0;
9064}
9065
Eric Andersenc470f442003-07-28 09:56:35 +00009066
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009067/* ============ input.c
9068 *
Eric Andersen90898442003-08-06 11:20:52 +00009069 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009070 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009071
Eric Andersenc470f442003-07-28 09:56:35 +00009072#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009073
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009074enum {
9075 INPUT_PUSH_FILE = 1,
9076 INPUT_NOFILE_OK = 2,
9077};
Eric Andersencb57d552001-06-28 07:25:16 +00009078
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009079static int plinno = 1; /* input line number */
9080/* number of characters left in input buffer */
9081static int parsenleft; /* copy of parsefile->nleft */
9082static int parselleft; /* copy of parsefile->lleft */
9083/* next character in input buffer */
9084static char *parsenextc; /* copy of parsefile->nextc */
9085
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009086static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009087/* values of checkkwd variable */
9088#define CHKALIAS 0x1
9089#define CHKKWD 0x2
9090#define CHKNL 0x4
9091
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009092static void
9093popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009094{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009095 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009096
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009097 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009098#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009099 if (sp->ap) {
9100 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9101 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009102 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009103 if (sp->string != sp->ap->val) {
9104 free(sp->string);
9105 }
9106 sp->ap->flag &= ~ALIASINUSE;
9107 if (sp->ap->flag & ALIASDEAD) {
9108 unalias(sp->ap->name);
9109 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009110 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009111#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009112 parsenextc = sp->prevstring;
9113 parsenleft = sp->prevnleft;
9114/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009115 g_parsefile->strpush = sp->prev;
9116 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009117 free(sp);
9118 INT_ON;
9119}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009120
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009121static int
9122preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009123{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009124 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009125 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009126 parsenextc = buf;
9127
Denis Vlasenko38f63192007-01-22 09:03:07 +00009128#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009129 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009130 if (!iflag || g_parsefile->fd)
9131 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009132 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009133#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009134 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009135#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009136 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9137 if (nr == 0) {
9138 /* Ctrl+C pressed */
9139 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009140 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009141 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009142 raise(SIGINT);
9143 return 1;
9144 }
Eric Andersenc470f442003-07-28 09:56:35 +00009145 goto retry;
9146 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009147 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009148 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009149 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009150 }
Eric Andersencb57d552001-06-28 07:25:16 +00009151 }
9152#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009153 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009154#endif
9155
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009156#if 0
9157/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009158 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009159 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009160 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009161 if (flags >= 0 && (flags & O_NONBLOCK)) {
9162 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009163 if (fcntl(0, F_SETFL, flags) >= 0) {
9164 out2str("sh: turning off NDELAY mode\n");
9165 goto retry;
9166 }
9167 }
9168 }
9169 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009170#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009171 return nr;
9172}
9173
9174/*
9175 * Refill the input buffer and return the next input character:
9176 *
9177 * 1) If a string was pushed back on the input, pop it;
9178 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9179 * from a string so we can't refill the buffer, return EOF.
9180 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9181 * 4) Process input up to the next newline, deleting nul characters.
9182 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009183static int
Eric Andersenc470f442003-07-28 09:56:35 +00009184preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009185{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009186 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009187 int more;
9188 char savec;
9189
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009190 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009191#if ENABLE_ASH_ALIAS
Denis Vlasenko16898402008-11-25 01:34:52 +00009192 if (parsenleft == -1 && g_parsefile->strpush->ap
9193 && parsenextc[-1] != ' ' && parsenextc[-1] != '\t'
9194 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009195 return PEOA;
9196 }
Eric Andersen2870d962001-07-02 17:27:21 +00009197#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009198 popstring();
9199 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009200 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009201 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009202 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009203 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009204 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009205
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009206 more = parselleft;
9207 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009208 again:
9209 more = preadfd();
9210 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009211 parselleft = parsenleft = EOF_NLEFT;
9212 return PEOF;
9213 }
9214 }
9215
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009216 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009217
9218 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009219 for (;;) {
9220 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009221
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009222 more--;
9223 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009224
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009225 if (!c)
9226 memmove(q, q + 1, more);
9227 else {
9228 q++;
9229 if (c == '\n') {
9230 parsenleft = q - parsenextc - 1;
9231 break;
9232 }
Eric Andersencb57d552001-06-28 07:25:16 +00009233 }
9234
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009235 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009236 parsenleft = q - parsenextc - 1;
9237 if (parsenleft < 0)
9238 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009239 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009240 }
9241 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009242 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009243
9244 savec = *q;
9245 *q = '\0';
9246
9247 if (vflag) {
9248 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009249 }
9250
9251 *q = savec;
9252
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009253 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009254}
9255
Denis Vlasenko834dee72008-10-07 09:18:30 +00009256#define pgetc_as_macro() (--parsenleft >= 0 ? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009257static int
9258pgetc(void)
9259{
9260 return pgetc_as_macro();
9261}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009262
9263#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009264#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009265#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009266#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009267#endif
9268
9269/*
9270 * Same as pgetc(), but ignores PEOA.
9271 */
9272#if ENABLE_ASH_ALIAS
9273static int
9274pgetc2(void)
9275{
9276 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009277 do {
Denis Vlasenko834dee72008-10-07 09:18:30 +00009278 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009279 } while (c == PEOA);
9280 return c;
9281}
9282#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009283#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009284#endif
9285
9286/*
9287 * Read a line from the script.
9288 */
9289static char *
9290pfgets(char *line, int len)
9291{
9292 char *p = line;
9293 int nleft = len;
9294 int c;
9295
9296 while (--nleft > 0) {
9297 c = pgetc2();
9298 if (c == PEOF) {
9299 if (p == line)
9300 return NULL;
9301 break;
9302 }
9303 *p++ = c;
9304 if (c == '\n')
9305 break;
9306 }
9307 *p = '\0';
9308 return line;
9309}
9310
Eric Andersenc470f442003-07-28 09:56:35 +00009311/*
9312 * Undo the last call to pgetc. Only one character may be pushed back.
9313 * PEOF may be pushed back.
9314 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009315static void
Eric Andersenc470f442003-07-28 09:56:35 +00009316pungetc(void)
9317{
Denis Vlasenko16898402008-11-25 01:34:52 +00009318 /* check is needed for ash -c 'echo 5&' + BASH_COMPAT to work */
9319 if (parsenleft < 0)
9320 return;
Eric Andersenc470f442003-07-28 09:56:35 +00009321 parsenleft++;
9322 parsenextc--;
9323}
Eric Andersencb57d552001-06-28 07:25:16 +00009324
9325/*
9326 * Push a string back onto the input at this current parsefile level.
9327 * We handle aliases this way.
9328 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009329#if !ENABLE_ASH_ALIAS
9330#define pushstring(s, ap) pushstring(s)
9331#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009332static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009333pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009334{
Eric Andersencb57d552001-06-28 07:25:16 +00009335 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009336 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009337
Eric Andersenc470f442003-07-28 09:56:35 +00009338 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009339 INT_OFF;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009340 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009341 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009342 sp->prev = g_parsefile->strpush;
9343 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009344 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009345 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009346 sp->prevstring = parsenextc;
9347 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009348#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009349 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009350 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009351 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009352 sp->string = s;
9353 }
Eric Andersen2870d962001-07-02 17:27:21 +00009354#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009355 parsenextc = s;
9356 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009357 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009358}
9359
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009360/*
9361 * To handle the "." command, a stack of input files is used. Pushfile
9362 * adds a new entry to the stack and popfile restores the previous level.
9363 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009364static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009365pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009366{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009367 struct parsefile *pf;
9368
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009369 g_parsefile->nleft = parsenleft;
9370 g_parsefile->lleft = parselleft;
9371 g_parsefile->nextc = parsenextc;
9372 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009373 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009374 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009375 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009376 /*pf->strpush = NULL; - ckzalloc did it */
9377 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009378 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009379}
9380
9381static void
9382popfile(void)
9383{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009384 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009385
Denis Vlasenkob012b102007-02-19 22:43:01 +00009386 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009387 if (pf->fd >= 0)
9388 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009389 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009390 while (pf->strpush)
9391 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009392 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009393 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009394 parsenleft = g_parsefile->nleft;
9395 parselleft = g_parsefile->lleft;
9396 parsenextc = g_parsefile->nextc;
9397 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009398 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009399}
9400
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009401/*
9402 * Return to top level.
9403 */
9404static void
9405popallfiles(void)
9406{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009407 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009408 popfile();
9409}
9410
9411/*
9412 * Close the file(s) that the shell is reading commands from. Called
9413 * after a fork is done.
9414 */
9415static void
9416closescript(void)
9417{
9418 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009419 if (g_parsefile->fd > 0) {
9420 close(g_parsefile->fd);
9421 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009422 }
9423}
9424
9425/*
9426 * Like setinputfile, but takes an open file descriptor. Call this with
9427 * interrupts off.
9428 */
9429static void
9430setinputfd(int fd, int push)
9431{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009432 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009433 if (push) {
9434 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009435 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009436 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009437 g_parsefile->fd = fd;
9438 if (g_parsefile->buf == NULL)
9439 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009440 parselleft = parsenleft = 0;
9441 plinno = 1;
9442}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009443
Eric Andersenc470f442003-07-28 09:56:35 +00009444/*
9445 * Set the input to take input from a file. If push is set, push the
9446 * old input onto the stack first.
9447 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009448static int
9449setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009450{
9451 int fd;
9452 int fd2;
9453
Denis Vlasenkob012b102007-02-19 22:43:01 +00009454 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009455 fd = open(fname, O_RDONLY);
9456 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009457 if (flags & INPUT_NOFILE_OK)
9458 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009459 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009460 }
Eric Andersenc470f442003-07-28 09:56:35 +00009461 if (fd < 10) {
9462 fd2 = copyfd(fd, 10);
9463 close(fd);
9464 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009465 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009466 fd = fd2;
9467 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009468 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009469 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009470 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009471 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009472}
9473
Eric Andersencb57d552001-06-28 07:25:16 +00009474/*
9475 * Like setinputfile, but takes input from a string.
9476 */
Eric Andersenc470f442003-07-28 09:56:35 +00009477static void
9478setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009479{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009480 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009481 pushfile();
9482 parsenextc = string;
9483 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009484 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009485 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009486 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009487}
9488
9489
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009490/* ============ mail.c
9491 *
9492 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009493 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009494
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009495#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009496
Eric Andersencb57d552001-06-28 07:25:16 +00009497#define MAXMBOXES 10
9498
Eric Andersenc470f442003-07-28 09:56:35 +00009499/* times of mailboxes */
9500static time_t mailtime[MAXMBOXES];
9501/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009502static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009503
Eric Andersencb57d552001-06-28 07:25:16 +00009504/*
Eric Andersenc470f442003-07-28 09:56:35 +00009505 * Print appropriate message(s) if mail has arrived.
9506 * If mail_var_path_changed is set,
9507 * then the value of MAIL has mail_var_path_changed,
9508 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009509 */
Eric Andersenc470f442003-07-28 09:56:35 +00009510static void
9511chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009512{
Eric Andersencb57d552001-06-28 07:25:16 +00009513 const char *mpath;
9514 char *p;
9515 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009516 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009517 struct stackmark smark;
9518 struct stat statb;
9519
Eric Andersencb57d552001-06-28 07:25:16 +00009520 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009521 mpath = mpathset() ? mpathval() : mailval();
9522 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009523 p = padvance(&mpath, nullstr);
9524 if (p == NULL)
9525 break;
9526 if (*p == '\0')
9527 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009528 for (q = p; *q; q++)
9529 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009530#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009531 if (q[-1] != '/')
9532 abort();
9533#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009534 q[-1] = '\0'; /* delete trailing '/' */
9535 if (stat(p, &statb) < 0) {
9536 *mtp = 0;
9537 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009538 }
Eric Andersenc470f442003-07-28 09:56:35 +00009539 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9540 fprintf(
9541 stderr, snlfmt,
9542 pathopt ? pathopt : "you have mail"
9543 );
9544 }
9545 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009546 }
Eric Andersenc470f442003-07-28 09:56:35 +00009547 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009548 popstackmark(&smark);
9549}
Eric Andersencb57d552001-06-28 07:25:16 +00009550
Eric Andersenc470f442003-07-28 09:56:35 +00009551static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009552changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009553{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009554 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009555}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009556
Denis Vlasenko131ae172007-02-18 13:00:19 +00009557#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009558
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009559
9560/* ============ ??? */
9561
Eric Andersencb57d552001-06-28 07:25:16 +00009562/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009563 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009564 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009565static void
9566setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009567{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009568 char **newparam;
9569 char **ap;
9570 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009571
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009572 for (nparam = 0; argv[nparam]; nparam++)
9573 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009574 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9575 while (*argv) {
9576 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009577 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009578 *ap = NULL;
9579 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009580 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009581 shellparam.nparam = nparam;
9582 shellparam.p = newparam;
9583#if ENABLE_ASH_GETOPTS
9584 shellparam.optind = 1;
9585 shellparam.optoff = -1;
9586#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009587}
9588
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009589/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009590 * Process shell options. The global variable argptr contains a pointer
9591 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009592 *
9593 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9594 * For a non-interactive shell, an error condition encountered
9595 * by a special built-in ... shall cause the shell to write a diagnostic message
9596 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009597 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009598 * ...
9599 * Utility syntax error (option or operand error) Shall exit
9600 * ...
9601 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9602 * we see that bash does not do that (set "finishes" with error code 1 instead,
9603 * and shell continues), and people rely on this behavior!
9604 * Testcase:
9605 * set -o barfoo 2>/dev/null
9606 * echo $?
9607 *
9608 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009609 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009610static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009611plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009612{
9613 int i;
9614
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009615 if (name) {
9616 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009617 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009618 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009619 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009620 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009621 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009622 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009623 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009624 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009625 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009626 if (val) {
9627 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9628 } else {
9629 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9630 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009631 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009632 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009633}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009634static void
9635setoption(int flag, int val)
9636{
9637 int i;
9638
9639 for (i = 0; i < NOPTS; i++) {
9640 if (optletters(i) == flag) {
9641 optlist[i] = val;
9642 return;
9643 }
9644 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009645 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009646 /* NOTREACHED */
9647}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009648static int
Eric Andersenc470f442003-07-28 09:56:35 +00009649options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009650{
9651 char *p;
9652 int val;
9653 int c;
9654
9655 if (cmdline)
9656 minusc = NULL;
9657 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009658 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009659 if (c != '-' && c != '+')
9660 break;
9661 argptr++;
9662 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009663 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009664 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009665 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009666 if (!cmdline) {
9667 /* "-" means turn off -x and -v */
9668 if (p[0] == '\0')
9669 xflag = vflag = 0;
9670 /* "--" means reset params */
9671 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009672 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009673 }
Eric Andersenc470f442003-07-28 09:56:35 +00009674 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009675 }
Eric Andersencb57d552001-06-28 07:25:16 +00009676 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009677 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009678 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009679 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009680 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009681 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009682 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009683 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009684 /* it already printed err message */
9685 return 1; /* error */
9686 }
Eric Andersencb57d552001-06-28 07:25:16 +00009687 if (*argptr)
9688 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009689 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9690 isloginsh = 1;
9691 /* bash does not accept +-login, we also won't */
9692 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009693 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009694 isloginsh = 1;
9695 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009696 } else {
9697 setoption(c, val);
9698 }
9699 }
9700 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009701 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009702}
9703
Eric Andersencb57d552001-06-28 07:25:16 +00009704/*
Eric Andersencb57d552001-06-28 07:25:16 +00009705 * The shift builtin command.
9706 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009707static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009708shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009709{
9710 int n;
9711 char **ap1, **ap2;
9712
9713 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009714 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009715 n = number(argv[1]);
9716 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009717 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009718 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009719 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009720 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009721 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009722 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009723 }
9724 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009725 while ((*ap2++ = *ap1++) != NULL)
9726 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009727#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009728 shellparam.optind = 1;
9729 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009730#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009731 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009732 return 0;
9733}
9734
Eric Andersencb57d552001-06-28 07:25:16 +00009735/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009736 * POSIX requires that 'set' (but not export or readonly) output the
9737 * variables in lexicographic order - by the locale's collating order (sigh).
9738 * Maybe we could keep them in an ordered balanced binary tree
9739 * instead of hashed lists.
9740 * For now just roll 'em through qsort for printing...
9741 */
9742static int
9743showvars(const char *sep_prefix, int on, int off)
9744{
9745 const char *sep;
9746 char **ep, **epend;
9747
9748 ep = listvars(on, off, &epend);
9749 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9750
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009751 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009752
9753 for (; ep < epend; ep++) {
9754 const char *p;
9755 const char *q;
9756
9757 p = strchrnul(*ep, '=');
9758 q = nullstr;
9759 if (*p)
9760 q = single_quote(++p);
9761 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9762 }
9763 return 0;
9764}
9765
9766/*
Eric Andersencb57d552001-06-28 07:25:16 +00009767 * The set command builtin.
9768 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009769static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009770setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009771{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009772 int retval;
9773
Denis Vlasenko68404f12008-03-17 09:00:54 +00009774 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009775 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009776 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009777 retval = 1;
9778 if (!options(0)) { /* if no parse error... */
9779 retval = 0;
9780 optschanged();
9781 if (*argptr != NULL) {
9782 setparam(argptr);
9783 }
Eric Andersencb57d552001-06-28 07:25:16 +00009784 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009785 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009786 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009787}
9788
Denis Vlasenko131ae172007-02-18 13:00:19 +00009789#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009790static void
9791change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009792{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009793 /* Galois LFSR parameter */
9794 /* Taps at 32 31 29 1: */
9795 enum { MASK = 0x8000000b };
9796 /* Another example - taps at 32 31 30 10: */
9797 /* MASK = 0x00400007 */
9798
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009799 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009800 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009801 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009802
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009803 /* LCG has period of 2^32 and alternating lowest bit */
9804 random_LCG = 1664525 * random_LCG + 1013904223;
9805 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9806 t = (random_galois_LFSR << 1);
9807 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9808 t ^= MASK;
9809 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009810 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009811 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009812 * for $RANDOM range. Combining with subtraction is
9813 * just for fun. + and ^ would work equally well. */
9814 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009815 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009816 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009817 vrandom.flags &= ~VNOFUNC;
9818 } else {
9819 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009820 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009821 }
Eric Andersenef02f822004-03-11 13:34:24 +00009822}
Eric Andersen16767e22004-03-16 05:14:10 +00009823#endif
9824
Denis Vlasenko131ae172007-02-18 13:00:19 +00009825#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009826static int
Eric Andersenc470f442003-07-28 09:56:35 +00009827getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009828{
9829 char *p, *q;
9830 char c = '?';
9831 int done = 0;
9832 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009833 char s[12];
9834 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009835
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009836 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009837 return 1;
9838 optnext = optfirst + *param_optind - 1;
9839
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009840 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009841 p = NULL;
9842 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009843 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009844 if (p == NULL || *p == '\0') {
9845 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009846 p = *optnext;
9847 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009848 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009849 p = NULL;
9850 done = 1;
9851 goto out;
9852 }
9853 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009854 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009855 goto atend;
9856 }
9857
9858 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009859 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009860 if (*q == '\0') {
9861 if (optstr[0] == ':') {
9862 s[0] = c;
9863 s[1] = '\0';
9864 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009865 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009866 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009867 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009868 }
9869 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009870 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009871 }
9872 if (*++q == ':')
9873 q++;
9874 }
9875
9876 if (*++q == ':') {
9877 if (*p == '\0' && (p = *optnext) == NULL) {
9878 if (optstr[0] == ':') {
9879 s[0] = c;
9880 s[1] = '\0';
9881 err |= setvarsafe("OPTARG", s, 0);
9882 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009883 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009884 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009885 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009886 c = '?';
9887 }
Eric Andersenc470f442003-07-28 09:56:35 +00009888 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009889 }
9890
9891 if (p == *optnext)
9892 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009893 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009894 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009895 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009896 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009897 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009898 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009899 *param_optind = optnext - optfirst + 1;
9900 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009901 err |= setvarsafe("OPTIND", s, VNOFUNC);
9902 s[0] = c;
9903 s[1] = '\0';
9904 err |= setvarsafe(optvar, s, 0);
9905 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009906 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009907 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009908 flush_stdout_stderr();
9909 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009910 }
9911 return done;
9912}
Eric Andersenc470f442003-07-28 09:56:35 +00009913
9914/*
9915 * The getopts builtin. Shellparam.optnext points to the next argument
9916 * to be processed. Shellparam.optptr points to the next character to
9917 * be processed in the current argument. If shellparam.optnext is NULL,
9918 * then it's the first time getopts has been called.
9919 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009920static int
Eric Andersenc470f442003-07-28 09:56:35 +00009921getoptscmd(int argc, char **argv)
9922{
9923 char **optbase;
9924
9925 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009926 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009927 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009928 optbase = shellparam.p;
9929 if (shellparam.optind > shellparam.nparam + 1) {
9930 shellparam.optind = 1;
9931 shellparam.optoff = -1;
9932 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009933 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009934 optbase = &argv[3];
9935 if (shellparam.optind > argc - 2) {
9936 shellparam.optind = 1;
9937 shellparam.optoff = -1;
9938 }
9939 }
9940
9941 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009942 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009943}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009944#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009945
Eric Andersencb57d552001-06-28 07:25:16 +00009946
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009947/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009948
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009949struct heredoc {
9950 struct heredoc *next; /* next here document in list */
9951 union node *here; /* redirection node */
9952 char *eofmark; /* string indicating end of input */
9953 smallint striptabs; /* if set, strip leading tabs */
9954};
9955
9956static smallint tokpushback; /* last token pushed back */
9957static smallint parsebackquote; /* nonzero if we are inside backquotes */
9958static smallint quoteflag; /* set if (part of) last token was quoted */
9959static token_id_t lasttoken; /* last token read (integer id Txxx) */
9960static struct heredoc *heredoclist; /* list of here documents to read */
9961static char *wordtext; /* text of last word returned by readtoken */
9962static struct nodelist *backquotelist;
9963static union node *redirnode;
9964static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009965/*
9966 * NEOF is returned by parsecmd when it encounters an end of file. It
9967 * must be distinct from NULL, so we use the address of a variable that
9968 * happens to be handy.
9969 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009970#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009971
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009972static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009973static void
9974raise_error_syntax(const char *msg)
9975{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009976 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009977 /* NOTREACHED */
9978}
9979
9980/*
9981 * Called when an unexpected token is read during the parse. The argument
9982 * is the token that is expected, or -1 if more than one type of token can
9983 * occur at this point.
9984 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009985static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009986static void
9987raise_error_unexpected_syntax(int token)
9988{
9989 char msg[64];
9990 int l;
9991
9992 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9993 if (token >= 0)
9994 sprintf(msg + l, " (expecting %s)", tokname(token));
9995 raise_error_syntax(msg);
9996 /* NOTREACHED */
9997}
Eric Andersencb57d552001-06-28 07:25:16 +00009998
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009999#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010000
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010001/* parsing is heavily cross-recursive, need these forward decls */
10002static union node *andor(void);
10003static union node *pipeline(void);
10004static union node *parse_command(void);
10005static void parseheredoc(void);
10006static char peektoken(void);
10007static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010008
Eric Andersenc470f442003-07-28 09:56:35 +000010009static union node *
10010list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010011{
10012 union node *n1, *n2, *n3;
10013 int tok;
10014
Eric Andersenc470f442003-07-28 09:56:35 +000010015 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10016 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010017 return NULL;
10018 n1 = NULL;
10019 for (;;) {
10020 n2 = andor();
10021 tok = readtoken();
10022 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010023 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010024 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010025 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010026 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010027 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010028 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010029 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010030 n2 = n3;
10031 }
10032 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010033 }
10034 }
10035 if (n1 == NULL) {
10036 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010037 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010038 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010039 n3->type = NSEMI;
10040 n3->nbinary.ch1 = n1;
10041 n3->nbinary.ch2 = n2;
10042 n1 = n3;
10043 }
10044 switch (tok) {
10045 case TBACKGND:
10046 case TSEMI:
10047 tok = readtoken();
10048 /* fall through */
10049 case TNL:
10050 if (tok == TNL) {
10051 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010052 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010053 return n1;
10054 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010055 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010056 }
Eric Andersenc470f442003-07-28 09:56:35 +000010057 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010058 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010059 return n1;
10060 break;
10061 case TEOF:
10062 if (heredoclist)
10063 parseheredoc();
10064 else
Eric Andersenc470f442003-07-28 09:56:35 +000010065 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010066 return n1;
10067 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010068 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010069 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010070 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010071 return n1;
10072 }
10073 }
10074}
10075
Eric Andersenc470f442003-07-28 09:56:35 +000010076static union node *
10077andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010078{
Eric Andersencb57d552001-06-28 07:25:16 +000010079 union node *n1, *n2, *n3;
10080 int t;
10081
Eric Andersencb57d552001-06-28 07:25:16 +000010082 n1 = pipeline();
10083 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010084 t = readtoken();
10085 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010086 t = NAND;
10087 } else if (t == TOR) {
10088 t = NOR;
10089 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010090 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010091 return n1;
10092 }
Eric Andersenc470f442003-07-28 09:56:35 +000010093 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010094 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010095 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010096 n3->type = t;
10097 n3->nbinary.ch1 = n1;
10098 n3->nbinary.ch2 = n2;
10099 n1 = n3;
10100 }
10101}
10102
Eric Andersenc470f442003-07-28 09:56:35 +000010103static union node *
10104pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010105{
Eric Andersencb57d552001-06-28 07:25:16 +000010106 union node *n1, *n2, *pipenode;
10107 struct nodelist *lp, *prev;
10108 int negate;
10109
10110 negate = 0;
10111 TRACE(("pipeline: entered\n"));
10112 if (readtoken() == TNOT) {
10113 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010114 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010115 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010116 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010117 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010118 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010119 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010120 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010121 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010122 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010123 pipenode->npipe.cmdlist = lp;
10124 lp->n = n1;
10125 do {
10126 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010127 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010128 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010129 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010130 prev->next = lp;
10131 } while (readtoken() == TPIPE);
10132 lp->next = NULL;
10133 n1 = pipenode;
10134 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010135 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010136 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010137 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010138 n2->type = NNOT;
10139 n2->nnot.com = n1;
10140 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010141 }
10142 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010143}
10144
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010145static union node *
10146makename(void)
10147{
10148 union node *n;
10149
Denis Vlasenko597906c2008-02-20 16:38:54 +000010150 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010151 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010152 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010153 n->narg.text = wordtext;
10154 n->narg.backquote = backquotelist;
10155 return n;
10156}
10157
10158static void
10159fixredir(union node *n, const char *text, int err)
10160{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010161 int fd;
10162
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010163 TRACE(("Fix redir %s %d\n", text, err));
10164 if (!err)
10165 n->ndup.vname = NULL;
10166
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010167 fd = bb_strtou(text, NULL, 10);
10168 if (!errno && fd >= 0)
10169 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010170 else if (LONE_DASH(text))
10171 n->ndup.dupfd = -1;
10172 else {
10173 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010174 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010175 n->ndup.vname = makename();
10176 }
10177}
10178
10179/*
10180 * Returns true if the text contains nothing to expand (no dollar signs
10181 * or backquotes).
10182 */
10183static int
10184noexpand(char *text)
10185{
10186 char *p;
10187 char c;
10188
10189 p = text;
10190 while ((c = *p++) != '\0') {
10191 if (c == CTLQUOTEMARK)
10192 continue;
10193 if (c == CTLESC)
10194 p++;
10195 else if (SIT(c, BASESYNTAX) == CCTL)
10196 return 0;
10197 }
10198 return 1;
10199}
10200
10201static void
10202parsefname(void)
10203{
10204 union node *n = redirnode;
10205
10206 if (readtoken() != TWORD)
10207 raise_error_unexpected_syntax(-1);
10208 if (n->type == NHERE) {
10209 struct heredoc *here = heredoc;
10210 struct heredoc *p;
10211 int i;
10212
10213 if (quoteflag == 0)
10214 n->type = NXHERE;
10215 TRACE(("Here document %d\n", n->type));
10216 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010217 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010218 rmescapes(wordtext);
10219 here->eofmark = wordtext;
10220 here->next = NULL;
10221 if (heredoclist == NULL)
10222 heredoclist = here;
10223 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010224 for (p = heredoclist; p->next; p = p->next)
10225 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010226 p->next = here;
10227 }
10228 } else if (n->type == NTOFD || n->type == NFROMFD) {
10229 fixredir(n, wordtext, 0);
10230 } else {
10231 n->nfile.fname = makename();
10232 }
10233}
Eric Andersencb57d552001-06-28 07:25:16 +000010234
Eric Andersenc470f442003-07-28 09:56:35 +000010235static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010236simplecmd(void)
10237{
10238 union node *args, **app;
10239 union node *n = NULL;
10240 union node *vars, **vpp;
10241 union node **rpp, *redir;
10242 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010243#if ENABLE_ASH_BASH_COMPAT
10244 smallint double_brackets_flag = 0;
10245#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010246
10247 args = NULL;
10248 app = &args;
10249 vars = NULL;
10250 vpp = &vars;
10251 redir = NULL;
10252 rpp = &redir;
10253
10254 savecheckkwd = CHKALIAS;
10255 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010256 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010257 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010258 t = readtoken();
10259 switch (t) {
10260#if ENABLE_ASH_BASH_COMPAT
10261 case TAND: /* "&&" */
10262 case TOR: /* "||" */
10263 if (!double_brackets_flag) {
10264 tokpushback = 1;
10265 goto out;
10266 }
10267 wordtext = (char *) (t == TAND ? "-a" : "-o");
10268#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010269 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010270 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010271 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010272 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010273 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010274#if ENABLE_ASH_BASH_COMPAT
10275 if (strcmp("[[", wordtext) == 0)
10276 double_brackets_flag = 1;
10277 else if (strcmp("]]", wordtext) == 0)
10278 double_brackets_flag = 0;
10279#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010280 n->narg.backquote = backquotelist;
10281 if (savecheckkwd && isassignment(wordtext)) {
10282 *vpp = n;
10283 vpp = &n->narg.next;
10284 } else {
10285 *app = n;
10286 app = &n->narg.next;
10287 savecheckkwd = 0;
10288 }
10289 break;
10290 case TREDIR:
10291 *rpp = n = redirnode;
10292 rpp = &n->nfile.next;
10293 parsefname(); /* read name of redirection file */
10294 break;
10295 case TLP:
10296 if (args && app == &args->narg.next
10297 && !vars && !redir
10298 ) {
10299 struct builtincmd *bcmd;
10300 const char *name;
10301
10302 /* We have a function */
10303 if (readtoken() != TRP)
10304 raise_error_unexpected_syntax(TRP);
10305 name = n->narg.text;
10306 if (!goodname(name)
10307 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10308 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010309 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010310 }
10311 n->type = NDEFUN;
10312 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10313 n->narg.next = parse_command();
10314 return n;
10315 }
10316 /* fall through */
10317 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010318 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010319 goto out;
10320 }
10321 }
10322 out:
10323 *app = NULL;
10324 *vpp = NULL;
10325 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010326 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010327 n->type = NCMD;
10328 n->ncmd.args = args;
10329 n->ncmd.assign = vars;
10330 n->ncmd.redirect = redir;
10331 return n;
10332}
10333
10334static union node *
10335parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010336{
Eric Andersencb57d552001-06-28 07:25:16 +000010337 union node *n1, *n2;
10338 union node *ap, **app;
10339 union node *cp, **cpp;
10340 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010341 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010342 int t;
10343
10344 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010345 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010346
Eric Andersencb57d552001-06-28 07:25:16 +000010347 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010348 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010349 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010350 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010351 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010352 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010353 n1->type = NIF;
10354 n1->nif.test = list(0);
10355 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010356 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010357 n1->nif.ifpart = list(0);
10358 n2 = n1;
10359 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010360 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010361 n2 = n2->nif.elsepart;
10362 n2->type = NIF;
10363 n2->nif.test = list(0);
10364 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010365 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010366 n2->nif.ifpart = list(0);
10367 }
10368 if (lasttoken == TELSE)
10369 n2->nif.elsepart = list(0);
10370 else {
10371 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010372 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010373 }
Eric Andersenc470f442003-07-28 09:56:35 +000010374 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010375 break;
10376 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010377 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010378 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010379 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010380 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010381 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010382 got = readtoken();
10383 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010384 TRACE(("expecting DO got %s %s\n", tokname(got),
10385 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010386 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010387 }
10388 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010389 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010390 break;
10391 }
10392 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010393 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010394 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010395 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010396 n1->type = NFOR;
10397 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010398 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010399 if (readtoken() == TIN) {
10400 app = &ap;
10401 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010402 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010403 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010404 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010405 n2->narg.text = wordtext;
10406 n2->narg.backquote = backquotelist;
10407 *app = n2;
10408 app = &n2->narg.next;
10409 }
10410 *app = NULL;
10411 n1->nfor.args = ap;
10412 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010413 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010414 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010415 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010416 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010417 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010418 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010419 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010420 n1->nfor.args = n2;
10421 /*
10422 * Newline or semicolon here is optional (but note
10423 * that the original Bourne shell only allowed NL).
10424 */
10425 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010426 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010427 }
Eric Andersenc470f442003-07-28 09:56:35 +000010428 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010429 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010430 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010431 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010432 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010433 break;
10434 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010435 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010436 n1->type = NCASE;
10437 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010438 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010439 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010440 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010441 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010442 n2->narg.text = wordtext;
10443 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010444 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010445 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010446 } while (readtoken() == TNL);
10447 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010448 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010449 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010450 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010451 checkkwd = CHKNL | CHKKWD;
10452 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010453 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010454 if (lasttoken == TLP)
10455 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010456 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010457 cp->type = NCLIST;
10458 app = &cp->nclist.pattern;
10459 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010460 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010461 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010462 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010463 ap->narg.text = wordtext;
10464 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010465 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010466 break;
10467 app = &ap->narg.next;
10468 readtoken();
10469 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010470 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010471 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010472 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010473 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010474
Eric Andersenc470f442003-07-28 09:56:35 +000010475 cpp = &cp->nclist.next;
10476
10477 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010478 t = readtoken();
10479 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010480 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010481 raise_error_unexpected_syntax(TENDCASE);
10482 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010483 }
Eric Andersenc470f442003-07-28 09:56:35 +000010484 }
Eric Andersencb57d552001-06-28 07:25:16 +000010485 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010486 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010487 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010488 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010489 n1->type = NSUBSHELL;
10490 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010491 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010492 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010493 break;
10494 case TBEGIN:
10495 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010496 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010497 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010498 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010499 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010500 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010501 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010502 }
10503
Eric Andersenc470f442003-07-28 09:56:35 +000010504 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010505 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010506
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010507 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010508 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010509 checkkwd = CHKKWD | CHKALIAS;
10510 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010511 while (readtoken() == TREDIR) {
10512 *rpp = n2 = redirnode;
10513 rpp = &n2->nfile.next;
10514 parsefname();
10515 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010516 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010517 *rpp = NULL;
10518 if (redir) {
10519 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010520 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010521 n2->type = NREDIR;
10522 n2->nredir.n = n1;
10523 n1 = n2;
10524 }
10525 n1->nredir.redirect = redir;
10526 }
Eric Andersencb57d552001-06-28 07:25:16 +000010527 return n1;
10528}
10529
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010530#if ENABLE_ASH_BASH_COMPAT
10531static int decode_dollar_squote(void)
10532{
10533 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10534 int c, cnt;
10535 char *p;
10536 char buf[4];
10537
10538 c = pgetc();
10539 p = strchr(C_escapes, c);
10540 if (p) {
10541 buf[0] = c;
10542 p = buf;
10543 cnt = 3;
10544 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10545 do {
10546 c = pgetc();
10547 *++p = c;
10548 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10549 pungetc();
10550 } else if (c == 'x') { /* \xHH */
10551 do {
10552 c = pgetc();
10553 *++p = c;
10554 } while (isxdigit(c) && --cnt);
10555 pungetc();
10556 if (cnt == 3) { /* \x but next char is "bad" */
10557 c = 'x';
10558 goto unrecognized;
10559 }
10560 } else { /* simple seq like \\ or \t */
10561 p++;
10562 }
10563 *p = '\0';
10564 p = buf;
10565 c = bb_process_escape_sequence((void*)&p);
10566 } else { /* unrecognized "\z": print both chars unless ' or " */
10567 if (c != '\'' && c != '"') {
10568 unrecognized:
10569 c |= 0x100; /* "please encode \, then me" */
10570 }
10571 }
10572 return c;
10573}
10574#endif
10575
Eric Andersencb57d552001-06-28 07:25:16 +000010576/*
10577 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10578 * is not NULL, read a here document. In the latter case, eofmark is the
10579 * word which marks the end of the document and striptabs is true if
10580 * leading tabs should be stripped from the document. The argument firstc
10581 * is the first character of the input token or document.
10582 *
10583 * Because C does not have internal subroutines, I have simulated them
10584 * using goto's to implement the subroutine linkage. The following macros
10585 * will run code that appears at the end of readtoken1.
10586 */
Eric Andersen2870d962001-07-02 17:27:21 +000010587#define CHECKEND() {goto checkend; checkend_return:;}
10588#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10589#define PARSESUB() {goto parsesub; parsesub_return:;}
10590#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10591#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10592#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010593static int
Eric Andersenc470f442003-07-28 09:56:35 +000010594readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010595{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010596 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010597 int c = firstc;
10598 char *out;
10599 int len;
10600 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010601 struct nodelist *bqlist;
10602 smallint quotef;
10603 smallint dblquote;
10604 smallint oldstyle;
10605 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010606#if ENABLE_ASH_EXPAND_PRMT
10607 smallint pssyntax; /* we are expanding a prompt string */
10608#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010609 int varnest; /* levels of variables expansion */
10610 int arinest; /* levels of arithmetic expansion */
10611 int parenlevel; /* levels of parens in arithmetic */
10612 int dqvarnest; /* levels of variables expansion within double quotes */
10613
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010614 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10615
Eric Andersencb57d552001-06-28 07:25:16 +000010616#if __GNUC__
10617 /* Avoid longjmp clobbering */
10618 (void) &out;
10619 (void) &quotef;
10620 (void) &dblquote;
10621 (void) &varnest;
10622 (void) &arinest;
10623 (void) &parenlevel;
10624 (void) &dqvarnest;
10625 (void) &oldstyle;
10626 (void) &prevsyntax;
10627 (void) &syntax;
10628#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010629 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010630 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010631 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010632 oldstyle = 0;
10633 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010634#if ENABLE_ASH_EXPAND_PRMT
10635 pssyntax = (syntax == PSSYNTAX);
10636 if (pssyntax)
10637 syntax = DQSYNTAX;
10638#endif
10639 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010640 varnest = 0;
10641 arinest = 0;
10642 parenlevel = 0;
10643 dqvarnest = 0;
10644
10645 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010646 loop:
10647 /* For each line, until end of word */
10648 {
Eric Andersenc470f442003-07-28 09:56:35 +000010649 CHECKEND(); /* set c to PEOF if at end of here document */
10650 for (;;) { /* until end of line or end of word */
10651 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010652 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010653 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010654 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010655 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010656 USTPUTC(c, out);
10657 plinno++;
10658 if (doprompt)
10659 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010660 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010661 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010662 case CWORD:
10663 USTPUTC(c, out);
10664 break;
10665 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010666 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010667 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010668#if ENABLE_ASH_BASH_COMPAT
10669 if (c == '\\' && bash_dollar_squote) {
10670 c = decode_dollar_squote();
10671 if (c & 0x100) {
10672 USTPUTC('\\', out);
10673 c = (unsigned char)c;
10674 }
10675 }
10676#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010677 USTPUTC(c, out);
10678 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010679 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010680 c = pgetc2();
10681 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010682 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010683 USTPUTC('\\', out);
10684 pungetc();
10685 } else if (c == '\n') {
10686 if (doprompt)
10687 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010688 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010689#if ENABLE_ASH_EXPAND_PRMT
10690 if (c == '$' && pssyntax) {
10691 USTPUTC(CTLESC, out);
10692 USTPUTC('\\', out);
10693 }
10694#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010695 if (dblquote && c != '\\'
10696 && c != '`' && c != '$'
10697 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010698 ) {
10699 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010700 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010701 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010702 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010703 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010704 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010705 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010706 }
10707 break;
10708 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010709 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010710 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010711 if (eofmark == NULL) {
10712 USTPUTC(CTLQUOTEMARK, out);
10713 }
Eric Andersencb57d552001-06-28 07:25:16 +000010714 break;
10715 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010716 syntax = DQSYNTAX;
10717 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010718 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010719 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010720 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010721 if (eofmark != NULL && arinest == 0
10722 && varnest == 0
10723 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010724 USTPUTC(c, out);
10725 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010726 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010727 syntax = BASESYNTAX;
10728 dblquote = 0;
10729 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010730 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010731 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010732 }
10733 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010734 case CVAR: /* '$' */
10735 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010736 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010737 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010738 if (varnest > 0) {
10739 varnest--;
10740 if (dqvarnest > 0) {
10741 dqvarnest--;
10742 }
10743 USTPUTC(CTLENDVAR, out);
10744 } else {
10745 USTPUTC(c, out);
10746 }
10747 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010748#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010749 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010750 parenlevel++;
10751 USTPUTC(c, out);
10752 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010753 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010754 if (parenlevel > 0) {
10755 USTPUTC(c, out);
10756 --parenlevel;
10757 } else {
10758 if (pgetc() == ')') {
10759 if (--arinest == 0) {
10760 USTPUTC(CTLENDARI, out);
10761 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010762 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010763 } else
10764 USTPUTC(')', out);
10765 } else {
10766 /*
10767 * unbalanced parens
10768 * (don't 2nd guess - no error)
10769 */
10770 pungetc();
10771 USTPUTC(')', out);
10772 }
10773 }
10774 break;
10775#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010776 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010777 PARSEBACKQOLD();
10778 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010779 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010780 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010781 case CIGN:
10782 break;
10783 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010784 if (varnest == 0) {
10785#if ENABLE_ASH_BASH_COMPAT
10786 if (c == '&') {
10787 if (pgetc() == '>')
10788 c = 0x100 + '>'; /* flag &> */
10789 pungetc();
10790 }
10791#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010792 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010793 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010794#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010795 if (c != PEOA)
10796#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010797 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010798
Eric Andersencb57d552001-06-28 07:25:16 +000010799 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010800 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010801 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010802 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010803 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010804#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010805 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010806 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010807#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010808 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010809 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010810 if (varnest != 0) {
10811 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010812 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010813 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010814 }
10815 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010816 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010817 out = stackblock();
10818 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010819 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10820 && quotef == 0
10821 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010822 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010823 PARSEREDIR(); /* passed as params: out, c */
10824 lasttoken = TREDIR;
10825 return lasttoken;
10826 }
10827 /* else: non-number X seen, interpret it
10828 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010829 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010830 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010831 }
10832 quoteflag = quotef;
10833 backquotelist = bqlist;
10834 grabstackblock(len);
10835 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010836 lasttoken = TWORD;
10837 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010838/* end of readtoken routine */
10839
Eric Andersencb57d552001-06-28 07:25:16 +000010840/*
10841 * Check to see whether we are at the end of the here document. When this
10842 * is called, c is set to the first character of the next input line. If
10843 * we are at the end of the here document, this routine sets the c to PEOF.
10844 */
Eric Andersenc470f442003-07-28 09:56:35 +000010845checkend: {
10846 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010847#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010848 if (c == PEOA) {
10849 c = pgetc2();
10850 }
10851#endif
10852 if (striptabs) {
10853 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010854 c = pgetc2();
10855 }
Eric Andersenc470f442003-07-28 09:56:35 +000010856 }
10857 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010858 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010859 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010860
Eric Andersenc470f442003-07-28 09:56:35 +000010861 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010862 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10863 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010864 if (*p == '\n' && *q == '\0') {
10865 c = PEOF;
10866 plinno++;
10867 needprompt = doprompt;
10868 } else {
10869 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010870 }
10871 }
10872 }
10873 }
Eric Andersenc470f442003-07-28 09:56:35 +000010874 goto checkend_return;
10875}
Eric Andersencb57d552001-06-28 07:25:16 +000010876
Eric Andersencb57d552001-06-28 07:25:16 +000010877/*
10878 * Parse a redirection operator. The variable "out" points to a string
10879 * specifying the fd to be redirected. The variable "c" contains the
10880 * first character of the redirection operator.
10881 */
Eric Andersenc470f442003-07-28 09:56:35 +000010882parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010883 /* out is already checked to be a valid number or "" */
10884 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010885 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010886
Denis Vlasenko597906c2008-02-20 16:38:54 +000010887 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010888 if (c == '>') {
10889 np->nfile.fd = 1;
10890 c = pgetc();
10891 if (c == '>')
10892 np->type = NAPPEND;
10893 else if (c == '|')
10894 np->type = NCLOBBER;
10895 else if (c == '&')
10896 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010897 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010898 else {
10899 np->type = NTO;
10900 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010901 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010902 }
10903#if ENABLE_ASH_BASH_COMPAT
10904 else if (c == 0x100 + '>') { /* this flags &> redirection */
10905 np->nfile.fd = 1;
10906 pgetc(); /* this is '>', no need to check */
10907 np->type = NTO2;
10908 }
10909#endif
10910 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 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011283 '\n', '(', ')', /* singles */
11284 '&', '|', ';', /* doubles */
11285 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011286};
Eric Andersencb57d552001-06-28 07:25:16 +000011287
Denis Vlasenko834dee72008-10-07 09:18:30 +000011288#define xxreadtoken_singles 3
11289#define xxreadtoken_doubles 3
11290
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011291static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011292 TNL, TLP, TRP, /* only single occurrence allowed */
11293 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11294 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011295 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011296};
11297
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011298static 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 */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011312 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011313 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11314 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011315
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011316 if (c == '#') {
11317 while ((c = pgetc()) != '\n' && c != PEOF)
11318 continue;
11319 pungetc();
11320 } else if (c == '\\') {
11321 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011322 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011323 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011324 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011325 startlinno = ++plinno;
11326 if (doprompt)
11327 setprompt(2);
11328 } else {
11329 const char *p;
11330
11331 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11332 if (c != PEOF) {
11333 if (c == '\n') {
11334 plinno++;
11335 needprompt = doprompt;
11336 }
11337
11338 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011339 if (p == NULL)
11340 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011341
Denis Vlasenko834dee72008-10-07 09:18:30 +000011342 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11343 int cc = pgetc();
11344 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011345 p += xxreadtoken_doubles + 1;
11346 } else {
11347 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011348#if ENABLE_ASH_BASH_COMPAT
11349 if (c == '&' && cc == '>') /* &> */
11350 break; /* return readtoken1(...) */
11351#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011352 }
11353 }
11354 }
11355 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11356 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011357 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011358 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011359
11360 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011361}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011362#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011363#define RETURN(token) return lasttoken = token
11364static int
11365xxreadtoken(void)
11366{
11367 int c;
11368
11369 if (tokpushback) {
11370 tokpushback = 0;
11371 return lasttoken;
11372 }
11373 if (needprompt) {
11374 setprompt(2);
11375 }
11376 startlinno = plinno;
11377 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011378 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011379 switch (c) {
11380 case ' ': case '\t':
11381#if ENABLE_ASH_ALIAS
11382 case PEOA:
11383#endif
11384 continue;
11385 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011386 while ((c = pgetc()) != '\n' && c != PEOF)
11387 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011388 pungetc();
11389 continue;
11390 case '\\':
11391 if (pgetc() == '\n') {
11392 startlinno = ++plinno;
11393 if (doprompt)
11394 setprompt(2);
11395 continue;
11396 }
11397 pungetc();
11398 goto breakloop;
11399 case '\n':
11400 plinno++;
11401 needprompt = doprompt;
11402 RETURN(TNL);
11403 case PEOF:
11404 RETURN(TEOF);
11405 case '&':
11406 if (pgetc() == '&')
11407 RETURN(TAND);
11408 pungetc();
11409 RETURN(TBACKGND);
11410 case '|':
11411 if (pgetc() == '|')
11412 RETURN(TOR);
11413 pungetc();
11414 RETURN(TPIPE);
11415 case ';':
11416 if (pgetc() == ';')
11417 RETURN(TENDCASE);
11418 pungetc();
11419 RETURN(TSEMI);
11420 case '(':
11421 RETURN(TLP);
11422 case ')':
11423 RETURN(TRP);
11424 default:
11425 goto breakloop;
11426 }
11427 }
11428 breakloop:
11429 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11430#undef RETURN
11431}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011432#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011433
11434static int
11435readtoken(void)
11436{
11437 int t;
11438#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011439 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011440#endif
11441
11442#if ENABLE_ASH_ALIAS
11443 top:
11444#endif
11445
11446 t = xxreadtoken();
11447
11448 /*
11449 * eat newlines
11450 */
11451 if (checkkwd & CHKNL) {
11452 while (t == TNL) {
11453 parseheredoc();
11454 t = xxreadtoken();
11455 }
11456 }
11457
11458 if (t != TWORD || quoteflag) {
11459 goto out;
11460 }
11461
11462 /*
11463 * check for keywords
11464 */
11465 if (checkkwd & CHKKWD) {
11466 const char *const *pp;
11467
11468 pp = findkwd(wordtext);
11469 if (pp) {
11470 lasttoken = t = pp - tokname_array;
11471 TRACE(("keyword %s recognized\n", tokname(t)));
11472 goto out;
11473 }
11474 }
11475
11476 if (checkkwd & CHKALIAS) {
11477#if ENABLE_ASH_ALIAS
11478 struct alias *ap;
11479 ap = lookupalias(wordtext, 1);
11480 if (ap != NULL) {
11481 if (*ap->val) {
11482 pushstring(ap->val, ap);
11483 }
11484 goto top;
11485 }
11486#endif
11487 }
11488 out:
11489 checkkwd = 0;
11490#if DEBUG
11491 if (!alreadyseen)
11492 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11493 else
11494 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11495#endif
11496 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011497}
11498
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011499static char
11500peektoken(void)
11501{
11502 int t;
11503
11504 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011505 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011506 return tokname_array[t][0];
11507}
Eric Andersencb57d552001-06-28 07:25:16 +000011508
11509/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011510 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11511 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011512 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011513static union node *
11514parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011515{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011516 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011517
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011518 tokpushback = 0;
11519 doprompt = interact;
11520 if (doprompt)
11521 setprompt(doprompt);
11522 needprompt = 0;
11523 t = readtoken();
11524 if (t == TEOF)
11525 return NEOF;
11526 if (t == TNL)
11527 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011528 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011529 return list(1);
11530}
11531
11532/*
11533 * Input any here documents.
11534 */
11535static void
11536parseheredoc(void)
11537{
11538 struct heredoc *here;
11539 union node *n;
11540
11541 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011542 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011543
11544 while (here) {
11545 if (needprompt) {
11546 setprompt(2);
11547 }
11548 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11549 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011550 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011551 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011552 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011553 n->narg.text = wordtext;
11554 n->narg.backquote = backquotelist;
11555 here->here->nhere.doc = n;
11556 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011557 }
Eric Andersencb57d552001-06-28 07:25:16 +000011558}
11559
11560
11561/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011562 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011563 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011564#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011565static const char *
11566expandstr(const char *ps)
11567{
11568 union node n;
11569
11570 /* XXX Fix (char *) cast. */
11571 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011572 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011573 popfile();
11574
11575 n.narg.type = NARG;
11576 n.narg.next = NULL;
11577 n.narg.text = wordtext;
11578 n.narg.backquote = backquotelist;
11579
11580 expandarg(&n, NULL, 0);
11581 return stackblock();
11582}
11583#endif
11584
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011585/*
11586 * Execute a command or commands contained in a string.
11587 */
11588static int
11589evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011590{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011591 union node *n;
11592 struct stackmark smark;
11593 int skip;
11594
11595 setinputstring(s);
11596 setstackmark(&smark);
11597
11598 skip = 0;
11599 while ((n = parsecmd(0)) != NEOF) {
11600 evaltree(n, 0);
11601 popstackmark(&smark);
11602 skip = evalskip;
11603 if (skip)
11604 break;
11605 }
11606 popfile();
11607
11608 skip &= mask;
11609 evalskip = skip;
11610 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011611}
11612
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011613/*
11614 * The eval command.
11615 */
11616static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011617evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011618{
11619 char *p;
11620 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011621
Denis Vlasenko68404f12008-03-17 09:00:54 +000011622 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011623 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011624 argv += 2;
11625 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011626 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011627 for (;;) {
11628 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011629 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011630 if (p == NULL)
11631 break;
11632 STPUTC(' ', concat);
11633 }
11634 STPUTC('\0', concat);
11635 p = grabstackstr(concat);
11636 }
11637 evalstring(p, ~SKIPEVAL);
11638
11639 }
11640 return exitstatus;
11641}
11642
11643/*
11644 * Read and execute commands. "Top" is nonzero for the top level command
11645 * loop; it turns on prompting if the shell is interactive.
11646 */
11647static int
11648cmdloop(int top)
11649{
11650 union node *n;
11651 struct stackmark smark;
11652 int inter;
11653 int numeof = 0;
11654
11655 TRACE(("cmdloop(%d) called\n", top));
11656 for (;;) {
11657 int skip;
11658
11659 setstackmark(&smark);
11660#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011661 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011662 showjobs(stderr, SHOW_CHANGED);
11663#endif
11664 inter = 0;
11665 if (iflag && top) {
11666 inter++;
11667#if ENABLE_ASH_MAIL
11668 chkmail();
11669#endif
11670 }
11671 n = parsecmd(inter);
11672 /* showtree(n); DEBUG */
11673 if (n == NEOF) {
11674 if (!top || numeof >= 50)
11675 break;
11676 if (!stoppedjobs()) {
11677 if (!Iflag)
11678 break;
11679 out2str("\nUse \"exit\" to leave shell.\n");
11680 }
11681 numeof++;
11682 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011683 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11684 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011685 numeof = 0;
11686 evaltree(n, 0);
11687 }
11688 popstackmark(&smark);
11689 skip = evalskip;
11690
11691 if (skip) {
11692 evalskip = 0;
11693 return skip & SKIPEVAL;
11694 }
11695 }
11696 return 0;
11697}
11698
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011699/*
11700 * Take commands from a file. To be compatible we should do a path
11701 * search for the file, which is necessary to find sub-commands.
11702 */
11703static char *
11704find_dot_file(char *name)
11705{
11706 char *fullname;
11707 const char *path = pathval();
11708 struct stat statb;
11709
11710 /* don't try this for absolute or relative paths */
11711 if (strchr(name, '/'))
11712 return name;
11713
11714 while ((fullname = padvance(&path, name)) != NULL) {
11715 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11716 /*
11717 * Don't bother freeing here, since it will
11718 * be freed by the caller.
11719 */
11720 return fullname;
11721 }
11722 stunalloc(fullname);
11723 }
11724
11725 /* not found in the PATH */
11726 ash_msg_and_raise_error("%s: not found", name);
11727 /* NOTREACHED */
11728}
11729
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011730static int
11731dotcmd(int argc, char **argv)
11732{
11733 struct strlist *sp;
11734 volatile struct shparam saveparam;
11735 int status = 0;
11736
11737 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011738 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011739
Denis Vlasenko68404f12008-03-17 09:00:54 +000011740 if (argv[1]) { /* That's what SVR2 does */
11741 char *fullname = find_dot_file(argv[1]);
11742 argv += 2;
11743 argc -= 2;
11744 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011745 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011746 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011747 shellparam.nparam = argc;
11748 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011749 };
11750
11751 setinputfile(fullname, INPUT_PUSH_FILE);
11752 commandname = fullname;
11753 cmdloop(0);
11754 popfile();
11755
Denis Vlasenko68404f12008-03-17 09:00:54 +000011756 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011757 freeparam(&shellparam);
11758 shellparam = saveparam;
11759 };
11760 status = exitstatus;
11761 }
11762 return status;
11763}
11764
11765static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011766exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011767{
11768 if (stoppedjobs())
11769 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011770 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011771 exitstatus = number(argv[1]);
11772 raise_exception(EXEXIT);
11773 /* NOTREACHED */
11774}
11775
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011776/*
11777 * Read a file containing shell functions.
11778 */
11779static void
11780readcmdfile(char *name)
11781{
11782 setinputfile(name, INPUT_PUSH_FILE);
11783 cmdloop(0);
11784 popfile();
11785}
11786
11787
Denis Vlasenkocc571512007-02-23 21:10:35 +000011788/* ============ find_command inplementation */
11789
11790/*
11791 * Resolve a command name. If you change this routine, you may have to
11792 * change the shellexec routine as well.
11793 */
11794static void
11795find_command(char *name, struct cmdentry *entry, int act, const char *path)
11796{
11797 struct tblentry *cmdp;
11798 int idx;
11799 int prev;
11800 char *fullname;
11801 struct stat statb;
11802 int e;
11803 int updatetbl;
11804 struct builtincmd *bcmd;
11805
11806 /* If name contains a slash, don't use PATH or hash table */
11807 if (strchr(name, '/') != NULL) {
11808 entry->u.index = -1;
11809 if (act & DO_ABS) {
11810 while (stat(name, &statb) < 0) {
11811#ifdef SYSV
11812 if (errno == EINTR)
11813 continue;
11814#endif
11815 entry->cmdtype = CMDUNKNOWN;
11816 return;
11817 }
11818 }
11819 entry->cmdtype = CMDNORMAL;
11820 return;
11821 }
11822
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011823/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011824
11825 updatetbl = (path == pathval());
11826 if (!updatetbl) {
11827 act |= DO_ALTPATH;
11828 if (strstr(path, "%builtin") != NULL)
11829 act |= DO_ALTBLTIN;
11830 }
11831
11832 /* If name is in the table, check answer will be ok */
11833 cmdp = cmdlookup(name, 0);
11834 if (cmdp != NULL) {
11835 int bit;
11836
11837 switch (cmdp->cmdtype) {
11838 default:
11839#if DEBUG
11840 abort();
11841#endif
11842 case CMDNORMAL:
11843 bit = DO_ALTPATH;
11844 break;
11845 case CMDFUNCTION:
11846 bit = DO_NOFUNC;
11847 break;
11848 case CMDBUILTIN:
11849 bit = DO_ALTBLTIN;
11850 break;
11851 }
11852 if (act & bit) {
11853 updatetbl = 0;
11854 cmdp = NULL;
11855 } else if (cmdp->rehash == 0)
11856 /* if not invalidated by cd, we're done */
11857 goto success;
11858 }
11859
11860 /* If %builtin not in path, check for builtin next */
11861 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011862 if (bcmd) {
11863 if (IS_BUILTIN_REGULAR(bcmd))
11864 goto builtin_success;
11865 if (act & DO_ALTPATH) {
11866 if (!(act & DO_ALTBLTIN))
11867 goto builtin_success;
11868 } else if (builtinloc <= 0) {
11869 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011870 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011871 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011872
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011873#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011874 {
11875 int applet_no = find_applet_by_name(name);
11876 if (applet_no >= 0) {
11877 entry->cmdtype = CMDNORMAL;
11878 entry->u.index = -2 - applet_no;
11879 return;
11880 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011881 }
11882#endif
11883
Denis Vlasenkocc571512007-02-23 21:10:35 +000011884 /* We have to search path. */
11885 prev = -1; /* where to start */
11886 if (cmdp && cmdp->rehash) { /* doing a rehash */
11887 if (cmdp->cmdtype == CMDBUILTIN)
11888 prev = builtinloc;
11889 else
11890 prev = cmdp->param.index;
11891 }
11892
11893 e = ENOENT;
11894 idx = -1;
11895 loop:
11896 while ((fullname = padvance(&path, name)) != NULL) {
11897 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011898 /* NB: code below will still use fullname
11899 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011900 idx++;
11901 if (pathopt) {
11902 if (prefix(pathopt, "builtin")) {
11903 if (bcmd)
11904 goto builtin_success;
11905 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011906 }
11907 if ((act & DO_NOFUNC)
11908 || !prefix(pathopt, "func")
11909 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011910 continue;
11911 }
11912 }
11913 /* if rehash, don't redo absolute path names */
11914 if (fullname[0] == '/' && idx <= prev) {
11915 if (idx < prev)
11916 continue;
11917 TRACE(("searchexec \"%s\": no change\n", name));
11918 goto success;
11919 }
11920 while (stat(fullname, &statb) < 0) {
11921#ifdef SYSV
11922 if (errno == EINTR)
11923 continue;
11924#endif
11925 if (errno != ENOENT && errno != ENOTDIR)
11926 e = errno;
11927 goto loop;
11928 }
11929 e = EACCES; /* if we fail, this will be the error */
11930 if (!S_ISREG(statb.st_mode))
11931 continue;
11932 if (pathopt) { /* this is a %func directory */
11933 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011934 /* NB: stalloc will return space pointed by fullname
11935 * (because we don't have any intervening allocations
11936 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011937 readcmdfile(fullname);
11938 cmdp = cmdlookup(name, 0);
11939 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11940 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11941 stunalloc(fullname);
11942 goto success;
11943 }
11944 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11945 if (!updatetbl) {
11946 entry->cmdtype = CMDNORMAL;
11947 entry->u.index = idx;
11948 return;
11949 }
11950 INT_OFF;
11951 cmdp = cmdlookup(name, 1);
11952 cmdp->cmdtype = CMDNORMAL;
11953 cmdp->param.index = idx;
11954 INT_ON;
11955 goto success;
11956 }
11957
11958 /* We failed. If there was an entry for this command, delete it */
11959 if (cmdp && updatetbl)
11960 delete_cmd_entry();
11961 if (act & DO_ERR)
11962 ash_msg("%s: %s", name, errmsg(e, "not found"));
11963 entry->cmdtype = CMDUNKNOWN;
11964 return;
11965
11966 builtin_success:
11967 if (!updatetbl) {
11968 entry->cmdtype = CMDBUILTIN;
11969 entry->u.cmd = bcmd;
11970 return;
11971 }
11972 INT_OFF;
11973 cmdp = cmdlookup(name, 1);
11974 cmdp->cmdtype = CMDBUILTIN;
11975 cmdp->param.cmd = bcmd;
11976 INT_ON;
11977 success:
11978 cmdp->rehash = 0;
11979 entry->cmdtype = cmdp->cmdtype;
11980 entry->u = cmdp->param;
11981}
11982
11983
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011984/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011985
Eric Andersencb57d552001-06-28 07:25:16 +000011986/*
Eric Andersencb57d552001-06-28 07:25:16 +000011987 * The trap builtin.
11988 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011989static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011990trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011991{
11992 char *action;
11993 char **ap;
11994 int signo;
11995
Eric Andersenc470f442003-07-28 09:56:35 +000011996 nextopt(nullstr);
11997 ap = argptr;
11998 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011999 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012000 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012001 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012002 single_quote(trap[signo]),
12003 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012004 }
12005 }
12006 return 0;
12007 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012008 action = NULL;
12009 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012010 action = *ap++;
12011 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012012 signo = get_signum(*ap);
12013 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012014 ash_msg_and_raise_error("%s: bad trap", *ap);
12015 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012016 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012017 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012018 action = NULL;
12019 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012020 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012021 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012022 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012023 trap[signo] = action;
12024 if (signo != 0)
12025 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012026 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012027 ap++;
12028 }
12029 return 0;
12030}
12031
Eric Andersenc470f442003-07-28 09:56:35 +000012032
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012033/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012034
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012035#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012036/*
12037 * Lists available builtins
12038 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012039static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012040helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012041{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012042 unsigned col;
12043 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012044
12045 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012046 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012047 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012048 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012049 if (col > 60) {
12050 out1fmt("\n");
12051 col = 0;
12052 }
12053 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012054#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012055 {
12056 const char *a = applet_names;
12057 while (*a) {
12058 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12059 if (col > 60) {
12060 out1fmt("\n");
12061 col = 0;
12062 }
12063 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012064 }
12065 }
12066#endif
12067 out1fmt("\n\n");
12068 return EXIT_SUCCESS;
12069}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012070#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012071
Eric Andersencb57d552001-06-28 07:25:16 +000012072/*
Eric Andersencb57d552001-06-28 07:25:16 +000012073 * The export and readonly commands.
12074 */
Eric Andersenc470f442003-07-28 09:56:35 +000012075static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012076exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012077{
12078 struct var *vp;
12079 char *name;
12080 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012081 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012082 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012083
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012084 if (nextopt("p") != 'p') {
12085 aptr = argptr;
12086 name = *aptr;
12087 if (name) {
12088 do {
12089 p = strchr(name, '=');
12090 if (p != NULL) {
12091 p++;
12092 } else {
12093 vp = *findvar(hashvar(name), name);
12094 if (vp) {
12095 vp->flags |= flag;
12096 continue;
12097 }
Eric Andersencb57d552001-06-28 07:25:16 +000012098 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012099 setvar(name, p, flag);
12100 } while ((name = *++aptr) != NULL);
12101 return 0;
12102 }
Eric Andersencb57d552001-06-28 07:25:16 +000012103 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012104 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012105 return 0;
12106}
12107
Eric Andersencb57d552001-06-28 07:25:16 +000012108/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012109 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012110 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012111static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012112unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012113{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012114 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012115
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012116 cmdp = cmdlookup(name, 0);
12117 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12118 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012119}
12120
Eric Andersencb57d552001-06-28 07:25:16 +000012121/*
Eric Andersencb57d552001-06-28 07:25:16 +000012122 * The unset builtin command. We unset the function before we unset the
12123 * variable to allow a function to be unset when there is a readonly variable
12124 * with the same name.
12125 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012126static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012127unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012128{
12129 char **ap;
12130 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012131 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012132 int ret = 0;
12133
12134 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012135 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012136 }
Eric Andersencb57d552001-06-28 07:25:16 +000012137
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012138 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012139 if (flag != 'f') {
12140 i = unsetvar(*ap);
12141 ret |= i;
12142 if (!(i & 2))
12143 continue;
12144 }
12145 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012146 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012147 }
Eric Andersenc470f442003-07-28 09:56:35 +000012148 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012149}
12150
12151
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012152/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012153
Eric Andersenc470f442003-07-28 09:56:35 +000012154#include <sys/times.h>
12155
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012156static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012157 ' ', offsetof(struct tms, tms_utime),
12158 '\n', offsetof(struct tms, tms_stime),
12159 ' ', offsetof(struct tms, tms_cutime),
12160 '\n', offsetof(struct tms, tms_cstime),
12161 0
12162};
Eric Andersencb57d552001-06-28 07:25:16 +000012163
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012164static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012165timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012166{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012167 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012168 const unsigned char *p;
12169 struct tms buf;
12170
12171 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012172 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012173
12174 p = timescmd_str;
12175 do {
12176 t = *(clock_t *)(((char *) &buf) + p[1]);
12177 s = t / clk_tck;
12178 out1fmt("%ldm%ld.%.3lds%c",
12179 s/60, s%60,
12180 ((t - s * clk_tck) * 1000) / clk_tck,
12181 p[0]);
12182 } while (*(p += 2));
12183
Eric Andersencb57d552001-06-28 07:25:16 +000012184 return 0;
12185}
12186
Denis Vlasenko131ae172007-02-18 13:00:19 +000012187#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012188static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012189dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012190{
Eric Andersened9ecf72004-06-22 08:29:45 +000012191 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012192 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012193
Denis Vlasenkob012b102007-02-19 22:43:01 +000012194 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012195 result = arith(s, &errcode);
12196 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012197 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012198 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012199 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012200 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012201 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012202 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012203 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012204 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012205 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012206
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012207 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012208}
Eric Andersenc470f442003-07-28 09:56:35 +000012209
Eric Andersenc470f442003-07-28 09:56:35 +000012210/*
Eric Andersen90898442003-08-06 11:20:52 +000012211 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12212 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12213 *
12214 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012215 */
12216static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012217letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012218{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012219 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012220
Denis Vlasenko68404f12008-03-17 09:00:54 +000012221 argv++;
12222 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012223 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012224 do {
12225 i = dash_arith(*argv);
12226 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012227
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012228 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012229}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012230#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012231
Eric Andersenc470f442003-07-28 09:56:35 +000012232
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012233/* ============ miscbltin.c
12234 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012235 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012236 */
12237
12238#undef rflag
12239
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012240#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012241typedef enum __rlimit_resource rlim_t;
12242#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012243
Eric Andersenc470f442003-07-28 09:56:35 +000012244/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012245 * The read builtin. Options:
12246 * -r Do not interpret '\' specially
12247 * -s Turn off echo (tty only)
12248 * -n NCHARS Read NCHARS max
12249 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12250 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12251 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012252 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012253 * TODO: bash also has:
12254 * -a ARRAY Read into array[0],[1],etc
12255 * -d DELIM End on DELIM char, not newline
12256 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012257 */
Eric Andersenc470f442003-07-28 09:56:35 +000012258static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012259readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012260{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012261 static const char *const arg_REPLY[] = { "REPLY", NULL };
12262
Eric Andersenc470f442003-07-28 09:56:35 +000012263 char **ap;
12264 int backslash;
12265 char c;
12266 int rflag;
12267 char *prompt;
12268 const char *ifs;
12269 char *p;
12270 int startword;
12271 int status;
12272 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012273 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012274#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012275 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012276 int silent = 0;
12277 struct termios tty, old_tty;
12278#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012279#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012280 unsigned end_ms = 0;
12281 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012282#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012283
12284 rflag = 0;
12285 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012286 while ((i = nextopt("p:u:r"
12287 USE_ASH_READ_TIMEOUT("t:")
12288 USE_ASH_READ_NCHARS("n:s")
12289 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012290 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012291 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012292 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012293 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012294#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012295 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012296 nchars = bb_strtou(optionarg, NULL, 10);
12297 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012298 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012299 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012300 break;
12301 case 's':
12302 silent = 1;
12303 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012304#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012305#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012306 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012307 timeout = bb_strtou(optionarg, NULL, 10);
12308 if (errno || timeout > UINT_MAX / 2048)
12309 ash_msg_and_raise_error("invalid timeout");
12310 timeout *= 1000;
12311#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012312 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012313 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012314 /* EINVAL means number is ok, but not terminated by NUL */
12315 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012316 char *p2;
12317 if (*++p) {
12318 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012319 ts.tv_usec = bb_strtou(p, &p2, 10);
12320 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012321 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012322 scale = p2 - p;
12323 /* normalize to usec */
12324 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012325 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012326 while (scale++ < 6)
12327 ts.tv_usec *= 10;
12328 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012329 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012330 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012331 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012332 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012333 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012334 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012335#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012336 break;
12337#endif
12338 case 'r':
12339 rflag = 1;
12340 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012341 case 'u':
12342 fd = bb_strtou(optionarg, NULL, 10);
12343 if (fd < 0 || errno)
12344 ash_msg_and_raise_error("invalid file descriptor");
12345 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012346 default:
12347 break;
12348 }
Eric Andersenc470f442003-07-28 09:56:35 +000012349 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012350 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012351 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012352 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012353 ap = argptr;
12354 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012355 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012356 ifs = bltinlookup("IFS");
12357 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012358 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012359#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012360 tcgetattr(fd, &tty);
12361 old_tty = tty;
12362 if (nchars || silent) {
12363 if (nchars) {
12364 tty.c_lflag &= ~ICANON;
12365 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012366 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012367 if (silent) {
12368 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12369 }
12370 /* if tcgetattr failed, tcsetattr will fail too.
12371 * Ignoring, it's harmless. */
12372 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012373 }
12374#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012375
Eric Andersenc470f442003-07-28 09:56:35 +000012376 status = 0;
12377 startword = 1;
12378 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012379#if ENABLE_ASH_READ_TIMEOUT
12380 if (timeout) /* NB: ensuring end_ms is nonzero */
12381 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12382#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012383 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012384 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012385#if ENABLE_ASH_READ_TIMEOUT
12386 if (end_ms) {
12387 struct pollfd pfd[1];
12388 pfd[0].fd = fd;
12389 pfd[0].events = POLLIN;
12390 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12391 if ((int)timeout <= 0 /* already late? */
12392 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12393 ) { /* timed out! */
12394#if ENABLE_ASH_READ_NCHARS
12395 tcsetattr(fd, TCSANOW, &old_tty);
12396#endif
12397 return 1;
12398 }
12399 }
12400#endif
12401 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012402 status = 1;
12403 break;
12404 }
12405 if (c == '\0')
12406 continue;
12407 if (backslash) {
12408 backslash = 0;
12409 if (c != '\n')
12410 goto put;
12411 continue;
12412 }
12413 if (!rflag && c == '\\') {
12414 backslash++;
12415 continue;
12416 }
12417 if (c == '\n')
12418 break;
12419 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12420 continue;
12421 }
12422 startword = 0;
12423 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12424 STACKSTRNUL(p);
12425 setvar(*ap, stackblock(), 0);
12426 ap++;
12427 startword = 1;
12428 STARTSTACKSTR(p);
12429 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012430 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012431 STPUTC(c, p);
12432 }
12433 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012434/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012435#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012436 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012437#else
12438 while (1);
12439#endif
12440
12441#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012442 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012443#endif
12444
Eric Andersenc470f442003-07-28 09:56:35 +000012445 STACKSTRNUL(p);
12446 /* Remove trailing blanks */
12447 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12448 *p = '\0';
12449 setvar(*ap, stackblock(), 0);
12450 while (*++ap != NULL)
12451 setvar(*ap, nullstr, 0);
12452 return status;
12453}
12454
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012455static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012456umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012457{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012458 static const char permuser[3] ALIGN1 = "ugo";
12459 static const char permmode[3] ALIGN1 = "rwx";
12460 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012461 S_IRUSR, S_IWUSR, S_IXUSR,
12462 S_IRGRP, S_IWGRP, S_IXGRP,
12463 S_IROTH, S_IWOTH, S_IXOTH
12464 };
12465
12466 char *ap;
12467 mode_t mask;
12468 int i;
12469 int symbolic_mode = 0;
12470
12471 while (nextopt("S") != '\0') {
12472 symbolic_mode = 1;
12473 }
12474
Denis Vlasenkob012b102007-02-19 22:43:01 +000012475 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012476 mask = umask(0);
12477 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012478 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012479
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012480 ap = *argptr;
12481 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012482 if (symbolic_mode) {
12483 char buf[18];
12484 char *p = buf;
12485
12486 for (i = 0; i < 3; i++) {
12487 int j;
12488
12489 *p++ = permuser[i];
12490 *p++ = '=';
12491 for (j = 0; j < 3; j++) {
12492 if ((mask & permmask[3 * i + j]) == 0) {
12493 *p++ = permmode[j];
12494 }
12495 }
12496 *p++ = ',';
12497 }
12498 *--p = 0;
12499 puts(buf);
12500 } else {
12501 out1fmt("%.4o\n", mask);
12502 }
12503 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012504 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012505 mask = 0;
12506 do {
12507 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012508 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012509 mask = (mask << 3) + (*ap - '0');
12510 } while (*++ap != '\0');
12511 umask(mask);
12512 } else {
12513 mask = ~mask & 0777;
12514 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012515 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012516 }
12517 umask(~mask & 0777);
12518 }
12519 }
12520 return 0;
12521}
12522
12523/*
12524 * ulimit builtin
12525 *
12526 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12527 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12528 * ash by J.T. Conklin.
12529 *
12530 * Public domain.
12531 */
12532
12533struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012534 uint8_t cmd; /* RLIMIT_xxx fit into it */
12535 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012536 char option;
12537};
12538
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012539static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012540#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012541 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012542#endif
12543#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012544 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012545#endif
12546#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012547 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012548#endif
12549#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012550 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012551#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012552#ifdef RLIMIT_CORE
12553 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012554#endif
12555#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012556 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012557#endif
12558#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012559 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012560#endif
12561#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012562 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012563#endif
12564#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012565 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012566#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012567#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012568 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012569#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012570#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012571 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012572#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012573};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012574static const char limits_name[] =
12575#ifdef RLIMIT_CPU
12576 "time(seconds)" "\0"
12577#endif
12578#ifdef RLIMIT_FSIZE
12579 "file(blocks)" "\0"
12580#endif
12581#ifdef RLIMIT_DATA
12582 "data(kb)" "\0"
12583#endif
12584#ifdef RLIMIT_STACK
12585 "stack(kb)" "\0"
12586#endif
12587#ifdef RLIMIT_CORE
12588 "coredump(blocks)" "\0"
12589#endif
12590#ifdef RLIMIT_RSS
12591 "memory(kb)" "\0"
12592#endif
12593#ifdef RLIMIT_MEMLOCK
12594 "locked memory(kb)" "\0"
12595#endif
12596#ifdef RLIMIT_NPROC
12597 "process" "\0"
12598#endif
12599#ifdef RLIMIT_NOFILE
12600 "nofiles" "\0"
12601#endif
12602#ifdef RLIMIT_AS
12603 "vmemory(kb)" "\0"
12604#endif
12605#ifdef RLIMIT_LOCKS
12606 "locks" "\0"
12607#endif
12608;
Eric Andersenc470f442003-07-28 09:56:35 +000012609
Glenn L McGrath76620622004-01-13 10:19:37 +000012610enum limtype { SOFT = 0x1, HARD = 0x2 };
12611
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012612static void
12613printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012614 const struct limits *l)
12615{
12616 rlim_t val;
12617
12618 val = limit->rlim_max;
12619 if (how & SOFT)
12620 val = limit->rlim_cur;
12621
12622 if (val == RLIM_INFINITY)
12623 out1fmt("unlimited\n");
12624 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012625 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012626 out1fmt("%lld\n", (long long) val);
12627 }
12628}
12629
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012630static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012631ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012632{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012633 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012634 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012635 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012636 const struct limits *l;
12637 int set, all = 0;
12638 int optc, what;
12639 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012640
12641 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012642 while ((optc = nextopt("HSa"
12643#ifdef RLIMIT_CPU
12644 "t"
12645#endif
12646#ifdef RLIMIT_FSIZE
12647 "f"
12648#endif
12649#ifdef RLIMIT_DATA
12650 "d"
12651#endif
12652#ifdef RLIMIT_STACK
12653 "s"
12654#endif
12655#ifdef RLIMIT_CORE
12656 "c"
12657#endif
12658#ifdef RLIMIT_RSS
12659 "m"
12660#endif
12661#ifdef RLIMIT_MEMLOCK
12662 "l"
12663#endif
12664#ifdef RLIMIT_NPROC
12665 "p"
12666#endif
12667#ifdef RLIMIT_NOFILE
12668 "n"
12669#endif
12670#ifdef RLIMIT_AS
12671 "v"
12672#endif
12673#ifdef RLIMIT_LOCKS
12674 "w"
12675#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012676 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012677 switch (optc) {
12678 case 'H':
12679 how = HARD;
12680 break;
12681 case 'S':
12682 how = SOFT;
12683 break;
12684 case 'a':
12685 all = 1;
12686 break;
12687 default:
12688 what = optc;
12689 }
12690
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012691 for (l = limits_tbl; l->option != what; l++)
12692 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012693
12694 set = *argptr ? 1 : 0;
12695 if (set) {
12696 char *p = *argptr;
12697
12698 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012699 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012700 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012701 val = RLIM_INFINITY;
12702 else {
12703 val = (rlim_t) 0;
12704
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012705 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012706 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012707 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012708 if (val < (rlim_t) 0)
12709 break;
12710 }
12711 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012712 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012713 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012714 }
12715 }
12716 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012717 const char *lname = limits_name;
12718 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012719 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012720 out1fmt("%-20s ", lname);
12721 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012722 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012723 }
12724 return 0;
12725 }
12726
12727 getrlimit(l->cmd, &limit);
12728 if (set) {
12729 if (how & HARD)
12730 limit.rlim_max = val;
12731 if (how & SOFT)
12732 limit.rlim_cur = val;
12733 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012734 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012735 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012736 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012737 }
12738 return 0;
12739}
12740
Eric Andersen90898442003-08-06 11:20:52 +000012741
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012742/* ============ Math support */
12743
Denis Vlasenko131ae172007-02-18 13:00:19 +000012744#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012745
12746/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12747
12748 Permission is hereby granted, free of charge, to any person obtaining
12749 a copy of this software and associated documentation files (the
12750 "Software"), to deal in the Software without restriction, including
12751 without limitation the rights to use, copy, modify, merge, publish,
12752 distribute, sublicense, and/or sell copies of the Software, and to
12753 permit persons to whom the Software is furnished to do so, subject to
12754 the following conditions:
12755
12756 The above copyright notice and this permission notice shall be
12757 included in all copies or substantial portions of the Software.
12758
12759 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12760 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12761 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12762 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12763 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12764 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12765 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12766*/
12767
12768/* This is my infix parser/evaluator. It is optimized for size, intended
12769 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012770 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012771 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012772 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012773 * be that which POSIX specifies for shells. */
12774
12775/* The code uses a simple two-stack algorithm. See
12776 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012777 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012778 * this is based (this code differs in that it applies operators immediately
12779 * to the stack instead of adding them to a queue to end up with an
12780 * expression). */
12781
12782/* To use the routine, call it with an expression string and error return
12783 * pointer */
12784
12785/*
12786 * Aug 24, 2001 Manuel Novoa III
12787 *
12788 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12789 *
12790 * 1) In arith_apply():
12791 * a) Cached values of *numptr and &(numptr[-1]).
12792 * b) Removed redundant test for zero denominator.
12793 *
12794 * 2) In arith():
12795 * a) Eliminated redundant code for processing operator tokens by moving
12796 * to a table-based implementation. Also folded handling of parens
12797 * into the table.
12798 * b) Combined all 3 loops which called arith_apply to reduce generated
12799 * code size at the cost of speed.
12800 *
12801 * 3) The following expressions were treated as valid by the original code:
12802 * 1() , 0! , 1 ( *3 ) .
12803 * These bugs have been fixed by internally enclosing the expression in
12804 * parens and then checking that all binary ops and right parens are
12805 * preceded by a valid expression (NUM_TOKEN).
12806 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012807 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012808 * ctype's isspace() if it is used by another busybox applet or if additional
12809 * whitespace chars should be considered. Look below the "#include"s for a
12810 * precompiler test.
12811 */
12812
12813/*
12814 * Aug 26, 2001 Manuel Novoa III
12815 *
12816 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12817 *
12818 * Merge in Aaron's comments previously posted to the busybox list,
12819 * modified slightly to take account of my changes to the code.
12820 *
12821 */
12822
12823/*
12824 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12825 *
12826 * - allow access to variable,
12827 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12828 * - realize assign syntax (VAR=expr, +=, *= etc)
12829 * - realize exponentiation (** operator)
12830 * - realize comma separated - expr, expr
12831 * - realise ++expr --expr expr++ expr--
12832 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012833 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012834 * - was restored loses XOR operator
12835 * - remove one goto label, added three ;-)
12836 * - protect $((num num)) as true zero expr (Manuel`s error)
12837 * - always use special isspace(), see comment from bash ;-)
12838 */
12839
Eric Andersen90898442003-08-06 11:20:52 +000012840#define arith_isspace(arithval) \
12841 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12842
Eric Andersen90898442003-08-06 11:20:52 +000012843typedef unsigned char operator;
12844
12845/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012846 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012847 * precedence. The ID portion is so that multiple operators can have the
12848 * same precedence, ensuring that the leftmost one is evaluated first.
12849 * Consider * and /. */
12850
12851#define tok_decl(prec,id) (((id)<<5)|(prec))
12852#define PREC(op) ((op) & 0x1F)
12853
12854#define TOK_LPAREN tok_decl(0,0)
12855
12856#define TOK_COMMA tok_decl(1,0)
12857
12858#define TOK_ASSIGN tok_decl(2,0)
12859#define TOK_AND_ASSIGN tok_decl(2,1)
12860#define TOK_OR_ASSIGN tok_decl(2,2)
12861#define TOK_XOR_ASSIGN tok_decl(2,3)
12862#define TOK_PLUS_ASSIGN tok_decl(2,4)
12863#define TOK_MINUS_ASSIGN tok_decl(2,5)
12864#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12865#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12866
12867#define TOK_MUL_ASSIGN tok_decl(3,0)
12868#define TOK_DIV_ASSIGN tok_decl(3,1)
12869#define TOK_REM_ASSIGN tok_decl(3,2)
12870
12871/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012872#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012873
12874/* conditional is right associativity too */
12875#define TOK_CONDITIONAL tok_decl(4,0)
12876#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12877
12878#define TOK_OR tok_decl(5,0)
12879
12880#define TOK_AND tok_decl(6,0)
12881
12882#define TOK_BOR tok_decl(7,0)
12883
12884#define TOK_BXOR tok_decl(8,0)
12885
12886#define TOK_BAND tok_decl(9,0)
12887
12888#define TOK_EQ tok_decl(10,0)
12889#define TOK_NE tok_decl(10,1)
12890
12891#define TOK_LT tok_decl(11,0)
12892#define TOK_GT tok_decl(11,1)
12893#define TOK_GE tok_decl(11,2)
12894#define TOK_LE tok_decl(11,3)
12895
12896#define TOK_LSHIFT tok_decl(12,0)
12897#define TOK_RSHIFT tok_decl(12,1)
12898
12899#define TOK_ADD tok_decl(13,0)
12900#define TOK_SUB tok_decl(13,1)
12901
12902#define TOK_MUL tok_decl(14,0)
12903#define TOK_DIV tok_decl(14,1)
12904#define TOK_REM tok_decl(14,2)
12905
12906/* exponent is right associativity */
12907#define TOK_EXPONENT tok_decl(15,1)
12908
12909/* For now unary operators. */
12910#define UNARYPREC 16
12911#define TOK_BNOT tok_decl(UNARYPREC,0)
12912#define TOK_NOT tok_decl(UNARYPREC,1)
12913
12914#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12915#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12916
12917#define PREC_PRE (UNARYPREC+2)
12918
12919#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12920#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12921
12922#define PREC_POST (UNARYPREC+3)
12923
12924#define TOK_POST_INC tok_decl(PREC_POST, 0)
12925#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12926
12927#define SPEC_PREC (UNARYPREC+4)
12928
12929#define TOK_NUM tok_decl(SPEC_PREC, 0)
12930#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12931
12932#define NUMPTR (*numstackptr)
12933
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012934static int
12935tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012936{
12937 operator prec = PREC(op);
12938
12939 convert_prec_is_assing(prec);
12940 return (prec == PREC(TOK_ASSIGN) ||
12941 prec == PREC_PRE || prec == PREC_POST);
12942}
12943
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012944static int
12945is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012946{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012947 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12948 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012949}
12950
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012951typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012952 arith_t val;
12953 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012954 char contidional_second_val_initialized;
12955 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012956 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012957} v_n_t;
12958
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012959typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012960 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012961 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012962} chk_var_recursive_looped_t;
12963
12964static chk_var_recursive_looped_t *prev_chk_var_recursive;
12965
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012966static int
12967arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012968{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012969 if (t->var) {
12970 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012971
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012972 if (p) {
12973 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012974
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012975 /* recursive try as expression */
12976 chk_var_recursive_looped_t *cur;
12977 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012978
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012979 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12980 if (strcmp(cur->var, t->var) == 0) {
12981 /* expression recursion loop detected */
12982 return -5;
12983 }
12984 }
12985 /* save current lookuped var name */
12986 cur = prev_chk_var_recursive;
12987 cur_save.var = t->var;
12988 cur_save.next = cur;
12989 prev_chk_var_recursive = &cur_save;
12990
12991 t->val = arith (p, &errcode);
12992 /* restore previous ptr after recursiving */
12993 prev_chk_var_recursive = cur;
12994 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012995 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012996 /* allow undefined var as 0 */
12997 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012998 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012999 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013000}
13001
13002/* "applying" a token means performing it on the top elements on the integer
13003 * stack. For a unary operator it will only change the top element, but a
13004 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013005static int
13006arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013007{
Eric Andersen90898442003-08-06 11:20:52 +000013008 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013009 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013010 int ret_arith_lookup_val;
13011
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013012 /* There is no operator that can work without arguments */
13013 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013014 numptr_m1 = NUMPTR - 1;
13015
13016 /* check operand is var with noninteger value */
13017 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013018 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013019 return ret_arith_lookup_val;
13020
13021 rez = numptr_m1->val;
13022 if (op == TOK_UMINUS)
13023 rez *= -1;
13024 else if (op == TOK_NOT)
13025 rez = !rez;
13026 else if (op == TOK_BNOT)
13027 rez = ~rez;
13028 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13029 rez++;
13030 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13031 rez--;
13032 else if (op != TOK_UPLUS) {
13033 /* Binary operators */
13034
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013035 /* check and binary operators need two arguments */
13036 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013037
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013038 /* ... and they pop one */
13039 --NUMPTR;
13040 numptr_val = rez;
13041 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013042 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013043 /* protect $((expr1 ? expr2)) without ": expr" */
13044 goto err;
13045 }
13046 rez = numptr_m1->contidional_second_val;
13047 } else if (numptr_m1->contidional_second_val_initialized) {
13048 /* protect $((expr1 : expr2)) without "expr ? " */
13049 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013050 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013051 numptr_m1 = NUMPTR - 1;
13052 if (op != TOK_ASSIGN) {
13053 /* check operand is var with noninteger value for not '=' */
13054 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13055 if (ret_arith_lookup_val)
13056 return ret_arith_lookup_val;
13057 }
13058 if (op == TOK_CONDITIONAL) {
13059 numptr_m1->contidional_second_val = rez;
13060 }
13061 rez = numptr_m1->val;
13062 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013063 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013064 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013065 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013066 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013067 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013068 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013069 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013070 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013071 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013072 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013073 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013074 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013075 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013076 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013077 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013078 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013079 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013080 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013081 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013082 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013083 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013084 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013085 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013087 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013088 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013089 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013090 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013091 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013092 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013093 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013094 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013095 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013096 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013097 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013098 /* protect $((expr : expr)) without "expr ? " */
13099 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013100 }
13101 numptr_m1->contidional_second_val_initialized = op;
13102 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013103 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013104 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013105 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013106 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013107 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013108 return -3; /* exponent less than 0 */
13109 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013110 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013111
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013112 if (numptr_val)
13113 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013114 c *= rez;
13115 rez = c;
13116 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013117 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013118 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013119 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013120 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013121 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013122 rez %= numptr_val;
13123 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013124 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013125 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013126
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013127 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013128 /* Hmm, 1=2 ? */
13129 goto err;
13130 }
13131 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013132#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013133 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013134#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013135 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013136#endif
Eric Andersen90898442003-08-06 11:20:52 +000013137 setvar(numptr_m1->var, buf, 0);
13138 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013139 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013140 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013141 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013142 rez++;
13143 }
13144 numptr_m1->val = rez;
13145 /* protect geting var value, is number now */
13146 numptr_m1->var = NULL;
13147 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013148 err:
13149 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013150}
13151
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013152/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013153static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013154 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13155 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13156 '<','<', 0, TOK_LSHIFT,
13157 '>','>', 0, TOK_RSHIFT,
13158 '|','|', 0, TOK_OR,
13159 '&','&', 0, TOK_AND,
13160 '!','=', 0, TOK_NE,
13161 '<','=', 0, TOK_LE,
13162 '>','=', 0, TOK_GE,
13163 '=','=', 0, TOK_EQ,
13164 '|','=', 0, TOK_OR_ASSIGN,
13165 '&','=', 0, TOK_AND_ASSIGN,
13166 '*','=', 0, TOK_MUL_ASSIGN,
13167 '/','=', 0, TOK_DIV_ASSIGN,
13168 '%','=', 0, TOK_REM_ASSIGN,
13169 '+','=', 0, TOK_PLUS_ASSIGN,
13170 '-','=', 0, TOK_MINUS_ASSIGN,
13171 '-','-', 0, TOK_POST_DEC,
13172 '^','=', 0, TOK_XOR_ASSIGN,
13173 '+','+', 0, TOK_POST_INC,
13174 '*','*', 0, TOK_EXPONENT,
13175 '!', 0, TOK_NOT,
13176 '<', 0, TOK_LT,
13177 '>', 0, TOK_GT,
13178 '=', 0, TOK_ASSIGN,
13179 '|', 0, TOK_BOR,
13180 '&', 0, TOK_BAND,
13181 '*', 0, TOK_MUL,
13182 '/', 0, TOK_DIV,
13183 '%', 0, TOK_REM,
13184 '+', 0, TOK_ADD,
13185 '-', 0, TOK_SUB,
13186 '^', 0, TOK_BXOR,
13187 /* uniq */
13188 '~', 0, TOK_BNOT,
13189 ',', 0, TOK_COMMA,
13190 '?', 0, TOK_CONDITIONAL,
13191 ':', 0, TOK_CONDITIONAL_SEP,
13192 ')', 0, TOK_RPAREN,
13193 '(', 0, TOK_LPAREN,
13194 0
13195};
13196/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013197#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013198
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013199static arith_t
13200arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013201{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013202 char arithval; /* Current character under analysis */
13203 operator lasttok, op;
13204 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013205 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013206 const char *p = endexpression;
13207 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013208 v_n_t *numstack, *numstackptr;
13209 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013210
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013211 /* Stack of integers */
13212 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13213 * in any given correct or incorrect expression is left as an exercise to
13214 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013215 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013216 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013217 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013218
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013219 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13220 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013221
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013222 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013223 arithval = *expr;
13224 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013225 if (p == endexpression) {
13226 /* Null expression. */
13227 return 0;
13228 }
13229
13230 /* This is only reached after all tokens have been extracted from the
13231 * input stream. If there are still tokens on the operator stack, they
13232 * are to be applied in order. At the end, there should be a final
13233 * result on the integer stack */
13234
13235 if (expr != endexpression + 1) {
13236 /* If we haven't done so already, */
13237 /* append a closing right paren */
13238 expr = endexpression;
13239 /* and let the loop process it. */
13240 continue;
13241 }
13242 /* At this point, we're done with the expression. */
13243 if (numstackptr != numstack+1) {
13244 /* ... but if there isn't, it's bad */
13245 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013246 *perrcode = -1;
13247 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013248 }
13249 if (numstack->var) {
13250 /* expression is $((var)) only, lookup now */
13251 errcode = arith_lookup_val(numstack);
13252 }
13253 ret:
13254 *perrcode = errcode;
13255 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013256 }
13257
Eric Andersen90898442003-08-06 11:20:52 +000013258 /* Continue processing the expression. */
13259 if (arith_isspace(arithval)) {
13260 /* Skip whitespace */
13261 goto prologue;
13262 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013263 p = endofname(expr);
13264 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013265 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013266
13267 numstackptr->var = alloca(var_name_size);
13268 safe_strncpy(numstackptr->var, expr, var_name_size);
13269 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013270 num:
Eric Andersen90898442003-08-06 11:20:52 +000013271 numstackptr->contidional_second_val_initialized = 0;
13272 numstackptr++;
13273 lasttok = TOK_NUM;
13274 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013275 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013276 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013277 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013278#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013279 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013280#else
13281 numstackptr->val = strtol(expr, (char **) &expr, 0);
13282#endif
Eric Andersen90898442003-08-06 11:20:52 +000013283 goto num;
13284 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013285 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013286 const char *o;
13287
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013288 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013289 /* strange operator not found */
13290 goto err;
13291 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013292 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013293 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013294 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013295 /* found */
13296 expr = o - 1;
13297 break;
13298 }
13299 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013300 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013301 p++;
13302 /* skip zero delim */
13303 p++;
13304 }
13305 op = p[1];
13306
13307 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013308 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13309 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013310
13311 /* Plus and minus are binary (not unary) _only_ if the last
13312 * token was as number, or a right paren (which pretends to be
13313 * a number, since it evaluates to one). Think about it.
13314 * It makes sense. */
13315 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013316 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013317 case TOK_ADD:
13318 op = TOK_UPLUS;
13319 break;
13320 case TOK_SUB:
13321 op = TOK_UMINUS;
13322 break;
13323 case TOK_POST_INC:
13324 op = TOK_PRE_INC;
13325 break;
13326 case TOK_POST_DEC:
13327 op = TOK_PRE_DEC;
13328 break;
Eric Andersen90898442003-08-06 11:20:52 +000013329 }
13330 }
13331 /* We don't want a unary operator to cause recursive descent on the
13332 * stack, because there can be many in a row and it could cause an
13333 * operator to be evaluated before its argument is pushed onto the
13334 * integer stack. */
13335 /* But for binary operators, "apply" everything on the operator
13336 * stack until we find an operator with a lesser priority than the
13337 * one we have just extracted. */
13338 /* Left paren is given the lowest priority so it will never be
13339 * "applied" in this way.
13340 * if associativity is right and priority eq, applied also skip
13341 */
13342 prec = PREC(op);
13343 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13344 /* not left paren or unary */
13345 if (lasttok != TOK_NUM) {
13346 /* binary op must be preceded by a num */
13347 goto err;
13348 }
13349 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013350 if (op == TOK_RPAREN) {
13351 /* The algorithm employed here is simple: while we don't
13352 * hit an open paren nor the bottom of the stack, pop
13353 * tokens and apply them */
13354 if (stackptr[-1] == TOK_LPAREN) {
13355 --stackptr;
13356 /* Any operator directly after a */
13357 lasttok = TOK_NUM;
13358 /* close paren should consider itself binary */
13359 goto prologue;
13360 }
13361 } else {
13362 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013363
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013364 convert_prec_is_assing(prec);
13365 convert_prec_is_assing(prev_prec);
13366 if (prev_prec < prec)
13367 break;
13368 /* check right assoc */
13369 if (prev_prec == prec && is_right_associativity(prec))
13370 break;
13371 }
13372 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13373 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013374 }
13375 if (op == TOK_RPAREN) {
13376 goto err;
13377 }
13378 }
13379
13380 /* Push this operator to the stack and remember it. */
13381 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013382 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013383 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013384 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013385}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013386#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013387
13388
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013389/* ============ main() and helpers */
13390
13391/*
13392 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013393 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013394static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013395static void
13396exitshell(void)
13397{
13398 struct jmploc loc;
13399 char *p;
13400 int status;
13401
13402 status = exitstatus;
13403 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13404 if (setjmp(loc.loc)) {
13405 if (exception == EXEXIT)
13406/* dash bug: it just does _exit(exitstatus) here
13407 * but we have to do setjobctl(0) first!
13408 * (bug is still not fixed in dash-0.5.3 - if you run dash
13409 * under Midnight Commander, on exit from dash MC is backgrounded) */
13410 status = exitstatus;
13411 goto out;
13412 }
13413 exception_handler = &loc;
13414 p = trap[0];
13415 if (p) {
13416 trap[0] = NULL;
13417 evalstring(p, 0);
13418 }
13419 flush_stdout_stderr();
13420 out:
13421 setjobctl(0);
13422 _exit(status);
13423 /* NOTREACHED */
13424}
13425
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013426static void
13427init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013428{
13429 /* from input.c: */
13430 basepf.nextc = basepf.buf = basebuf;
13431
13432 /* from trap.c: */
13433 signal(SIGCHLD, SIG_DFL);
13434
13435 /* from var.c: */
13436 {
13437 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013438 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013439 const char *p;
13440 struct stat st1, st2;
13441
13442 initvar();
13443 for (envp = environ; envp && *envp; envp++) {
13444 if (strchr(*envp, '=')) {
13445 setvareq(*envp, VEXPORT|VTEXTFIXED);
13446 }
13447 }
13448
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013449 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013450 setvar("PPID", ppid, 0);
13451
13452 p = lookupvar("PWD");
13453 if (p)
13454 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13455 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13456 p = '\0';
13457 setpwd(p, 0);
13458 }
13459}
13460
13461/*
13462 * Process the shell command line arguments.
13463 */
13464static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013465procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013466{
13467 int i;
13468 const char *xminusc;
13469 char **xargv;
13470
13471 xargv = argv;
13472 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013473 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013474 xargv++;
13475 for (i = 0; i < NOPTS; i++)
13476 optlist[i] = 2;
13477 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013478 if (options(1)) {
13479 /* it already printed err message */
13480 raise_exception(EXERROR);
13481 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013482 xargv = argptr;
13483 xminusc = minusc;
13484 if (*xargv == NULL) {
13485 if (xminusc)
13486 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13487 sflag = 1;
13488 }
13489 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13490 iflag = 1;
13491 if (mflag == 2)
13492 mflag = iflag;
13493 for (i = 0; i < NOPTS; i++)
13494 if (optlist[i] == 2)
13495 optlist[i] = 0;
13496#if DEBUG == 2
13497 debug = 1;
13498#endif
13499 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13500 if (xminusc) {
13501 minusc = *xargv++;
13502 if (*xargv)
13503 goto setarg0;
13504 } else if (!sflag) {
13505 setinputfile(*xargv, 0);
13506 setarg0:
13507 arg0 = *xargv++;
13508 commandname = arg0;
13509 }
13510
13511 shellparam.p = xargv;
13512#if ENABLE_ASH_GETOPTS
13513 shellparam.optind = 1;
13514 shellparam.optoff = -1;
13515#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013516 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013517 while (*xargv) {
13518 shellparam.nparam++;
13519 xargv++;
13520 }
13521 optschanged();
13522}
13523
13524/*
13525 * Read /etc/profile or .profile.
13526 */
13527static void
13528read_profile(const char *name)
13529{
13530 int skip;
13531
13532 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13533 return;
13534 skip = cmdloop(0);
13535 popfile();
13536 if (skip)
13537 exitshell();
13538}
13539
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013540/*
13541 * This routine is called when an error or an interrupt occurs in an
13542 * interactive shell and control is returned to the main command loop.
13543 */
13544static void
13545reset(void)
13546{
13547 /* from eval.c: */
13548 evalskip = 0;
13549 loopnest = 0;
13550 /* from input.c: */
13551 parselleft = parsenleft = 0; /* clear input buffer */
13552 popallfiles();
13553 /* from parser.c: */
13554 tokpushback = 0;
13555 checkkwd = 0;
13556 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013557 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013558}
13559
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013560#if PROFILE
13561static short profile_buf[16384];
13562extern int etext();
13563#endif
13564
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013565/*
13566 * Main routine. We initialize things, parse the arguments, execute
13567 * profiles if we're a login shell, and then call cmdloop to execute
13568 * commands. The setjmp call sets up the location to jump to when an
13569 * exception occurs. When an exception occurs the variable "state"
13570 * is used to figure out how far we had gotten.
13571 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013572int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013573int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013574{
13575 char *shinit;
13576 volatile int state;
13577 struct jmploc jmploc;
13578 struct stackmark smark;
13579
Denis Vlasenko01631112007-12-16 17:20:38 +000013580 /* Initialize global data */
13581 INIT_G_misc();
13582 INIT_G_memstack();
13583 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013584#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013585 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013586#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013587 INIT_G_cmdtable();
13588
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013589#if PROFILE
13590 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13591#endif
13592
13593#if ENABLE_FEATURE_EDITING
13594 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13595#endif
13596 state = 0;
13597 if (setjmp(jmploc.loc)) {
13598 int e;
13599 int s;
13600
13601 reset();
13602
13603 e = exception;
13604 if (e == EXERROR)
13605 exitstatus = 2;
13606 s = state;
13607 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13608 exitshell();
13609
13610 if (e == EXINT) {
13611 outcslow('\n', stderr);
13612 }
13613 popstackmark(&smark);
13614 FORCE_INT_ON; /* enable interrupts */
13615 if (s == 1)
13616 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013617 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013618 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013619 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013620 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013621 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013622 }
13623 exception_handler = &jmploc;
13624#if DEBUG
13625 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013626 trace_puts("Shell args: ");
13627 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013628#endif
13629 rootpid = getpid();
13630
13631#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013632 /* Can use monotonic_ns() for better randomness but for now it is
13633 * not used anywhere else in busybox... so avoid bloat */
13634 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013635#endif
13636 init();
13637 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013638 procargs(argv);
13639
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013640#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13641 if (iflag) {
13642 const char *hp = lookupvar("HISTFILE");
13643
13644 if (hp == NULL) {
13645 hp = lookupvar("HOME");
13646 if (hp != NULL) {
13647 char *defhp = concat_path_file(hp, ".ash_history");
13648 setvar("HISTFILE", defhp, 0);
13649 free(defhp);
13650 }
13651 }
13652 }
13653#endif
13654 if (argv[0] && argv[0][0] == '-')
13655 isloginsh = 1;
13656 if (isloginsh) {
13657 state = 1;
13658 read_profile("/etc/profile");
13659 state1:
13660 state = 2;
13661 read_profile(".profile");
13662 }
13663 state2:
13664 state = 3;
13665 if (
13666#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013667 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013668#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013669 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013670 ) {
13671 shinit = lookupvar("ENV");
13672 if (shinit != NULL && *shinit != '\0') {
13673 read_profile(shinit);
13674 }
13675 }
13676 state3:
13677 state = 4;
13678 if (minusc)
13679 evalstring(minusc, 0);
13680
13681 if (sflag || minusc == NULL) {
13682#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013683 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013684 const char *hp = lookupvar("HISTFILE");
13685
13686 if (hp != NULL)
13687 line_input_state->hist_file = hp;
13688 }
13689#endif
13690 state4: /* XXX ??? - why isn't this before the "if" statement */
13691 cmdloop(1);
13692 }
13693#if PROFILE
13694 monitor(0);
13695#endif
13696#ifdef GPROF
13697 {
13698 extern void _mcleanup(void);
13699 _mcleanup();
13700 }
13701#endif
13702 exitshell();
13703 /* NOTREACHED */
13704}
13705
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013706#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013707const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013708int main(int argc, char **argv)
13709{
13710 return ash_main(argc, argv);
13711}
13712#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013713
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013714
Eric Andersendf82f612001-06-28 07:46:40 +000013715/*-
13716 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013717 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013718 *
13719 * This code is derived from software contributed to Berkeley by
13720 * Kenneth Almquist.
13721 *
13722 * Redistribution and use in source and binary forms, with or without
13723 * modification, are permitted provided that the following conditions
13724 * are met:
13725 * 1. Redistributions of source code must retain the above copyright
13726 * notice, this list of conditions and the following disclaimer.
13727 * 2. Redistributions in binary form must reproduce the above copyright
13728 * notice, this list of conditions and the following disclaimer in the
13729 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013730 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013731 * may be used to endorse or promote products derived from this software
13732 * without specific prior written permission.
13733 *
13734 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13735 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13736 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13737 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13738 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13739 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13740 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13741 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13742 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13743 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13744 * SUCH DAMAGE.
13745 */