blob: d63acc2c84b5cdcd33a9a958bbacbf07ab09b6c4 [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 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersenc470f442003-07-28 09:56:35 +000045#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000046
47#define IFS_BROKEN
48
49#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000050
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000052#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#define _GNU_SOURCE
54#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000055#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000056
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000057#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000061#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000062#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000063#endif
64
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000065#ifndef PIPE_BUF
66#define PIPE_BUF 4096 /* amount of buffering in a pipe */
67#endif
68
Denis Vlasenkob012b102007-02-19 22:43:01 +000069#if defined(__uClinux__)
70#error "Do not even bother, ash will not run on uClinux"
71#endif
72
Denis Vlasenkob012b102007-02-19 22:43:01 +000073
Denis Vlasenko01631112007-12-16 17:20:38 +000074/* ============ Hash table sizes. Configurable. */
75
76#define VTABSIZE 39
77#define ATABSIZE 39
78#define CMDTABLESIZE 31 /* should be prime */
79
80
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000081/* ============ Misc helpers */
82
83#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
84
85/* C99 say: "char" declaration may be signed or unsigned default */
86#define signed_char2int(sc) ((int)((signed char)sc))
87
88
Denis Vlasenkob012b102007-02-19 22:43:01 +000089/* ============ Shell options */
90
91static const char *const optletters_optnames[] = {
92 "e" "errexit",
93 "f" "noglob",
94 "I" "ignoreeof",
95 "i" "interactive",
96 "m" "monitor",
97 "n" "noexec",
98 "s" "stdin",
99 "x" "xtrace",
100 "v" "verbose",
101 "C" "noclobber",
102 "a" "allexport",
103 "b" "notify",
104 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000105 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000106#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000107 ,"\0" "nolog"
108 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000109#endif
110};
111
112#define optletters(n) optletters_optnames[(n)][0]
113#define optnames(n) (&optletters_optnames[(n)][1])
114
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000115enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000116
Eric Andersenc470f442003-07-28 09:56:35 +0000117
Denis Vlasenkob012b102007-02-19 22:43:01 +0000118/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000119
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000120static const char homestr[] ALIGN1 = "HOME";
121static const char snlfmt[] ALIGN1 = "%s\n";
122static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000123
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000124/*
Eric Andersenc470f442003-07-28 09:56:35 +0000125 * We enclose jmp_buf in a structure so that we can declare pointers to
126 * jump locations. The global variable handler contains the location to
127 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000128 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000129 * exception handlers, the user should save the value of handler on entry
130 * to an inner scope, set handler to point to a jmploc structure for the
131 * inner scope, and restore handler on exit from the scope.
132 */
Eric Andersenc470f442003-07-28 09:56:35 +0000133struct jmploc {
134 jmp_buf loc;
135};
Denis Vlasenko01631112007-12-16 17:20:38 +0000136
137struct globals_misc {
138 /* pid of main shell */
139 int rootpid;
140 /* shell level: 0 for the main shell, 1 for its children, and so on */
141 int shlvl;
142#define rootshell (!shlvl)
143 char *minusc; /* argument to -c option */
144
145 char *curdir; // = nullstr; /* current working directory */
146 char *physdir; // = nullstr; /* physical working directory */
147
148 char *arg0; /* value of $0 */
149
150 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000151
152// disabled by vda: cannot understand how it was supposed to work -
153// cannot fix bugs. That's why you have to explain your non-trivial designs!
154// /* do we generate EXSIG events */
155// int exsig; /* counter */
156 volatile int suppressint; /* counter */
157 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
158 /* last pending signal */
159 volatile /*sig_atomic_t*/ smallint pendingsig;
160 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000161 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000162#define EXINT 0 /* SIGINT received */
163#define EXERROR 1 /* a generic error */
164#define EXSHELLPROC 2 /* execute a shell procedure */
165#define EXEXEC 3 /* command execution failed */
166#define EXEXIT 4 /* exit the shell */
167#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000168
Denis Vlasenko01631112007-12-16 17:20:38 +0000169 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000170 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000171
172 char optlist[NOPTS];
173#define eflag optlist[0]
174#define fflag optlist[1]
175#define Iflag optlist[2]
176#define iflag optlist[3]
177#define mflag optlist[4]
178#define nflag optlist[5]
179#define sflag optlist[6]
180#define xflag optlist[7]
181#define vflag optlist[8]
182#define Cflag optlist[9]
183#define aflag optlist[10]
184#define bflag optlist[11]
185#define uflag optlist[12]
186#define viflag optlist[13]
187#if DEBUG
188#define nolog optlist[14]
189#define debug optlist[15]
190#endif
191
192 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000193 /*
194 * Sigmode records the current value of the signal handlers for the various
195 * modes. A value of zero means that the current handler is not known.
196 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
197 */
198 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000199#define S_DFL 1 /* default signal handling (SIG_DFL) */
200#define S_CATCH 2 /* signal is caught */
201#define S_IGN 3 /* signal is ignored (SIG_IGN) */
202#define S_HARD_IGN 4 /* signal is ignored permenantly */
203#define S_RESET 5 /* temporary - to reset a hard ignored sig */
204
Denis Vlasenko01631112007-12-16 17:20:38 +0000205 /* indicates specified signal received */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +0000206 char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000207 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000208
209 /* Rarely referenced stuff */
210#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000211 /* Random number generators */
212 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
213 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000214#endif
215 pid_t backgndpid; /* pid of last background process */
216 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000217};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000218extern struct globals_misc *const ash_ptr_to_globals_misc;
219#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000220#define rootpid (G_misc.rootpid )
221#define shlvl (G_misc.shlvl )
222#define minusc (G_misc.minusc )
223#define curdir (G_misc.curdir )
224#define physdir (G_misc.physdir )
225#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000226#define exception_handler (G_misc.exception_handler)
227#define exception (G_misc.exception )
228#define suppressint (G_misc.suppressint )
229#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000230//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000231#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000232#define isloginsh (G_misc.isloginsh )
233#define nullstr (G_misc.nullstr )
234#define optlist (G_misc.optlist )
235#define sigmode (G_misc.sigmode )
236#define gotsig (G_misc.gotsig )
237#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000238#define random_galois_LFSR (G_misc.random_galois_LFSR)
239#define random_LCG (G_misc.random_LCG )
240#define backgndpid (G_misc.backgndpid )
241#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000242#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000243 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
244 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000245 curdir = nullstr; \
246 physdir = nullstr; \
247} while (0)
248
249
250/* ============ Interrupts / exceptions */
251
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000252/*
Eric Andersen2870d962001-07-02 17:27:21 +0000253 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000254 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000255 * much more efficient and portable. (But hacking the kernel is so much
256 * more fun than worrying about efficiency and portability. :-))
257 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000258#define INT_OFF do { \
259 suppressint++; \
260 xbarrier(); \
261} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000262
263/*
264 * Called to raise an exception. Since C doesn't include exceptions, we
265 * just do a longjmp to the exception handler. The type of exception is
266 * stored in the global variable "exception".
267 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000268static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000269static void
270raise_exception(int e)
271{
272#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000273 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000274 abort();
275#endif
276 INT_OFF;
277 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000278 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000279}
280
281/*
282 * Called from trap.c when a SIGINT is received. (If the user specifies
283 * that SIGINT is to be trapped or ignored using the trap builtin, then
284 * this routine is not called.) Suppressint is nonzero when interrupts
285 * are held using the INT_OFF macro. (The test for iflag is just
286 * defensive programming.)
287 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000288static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000289static void
290raise_interrupt(void)
291{
292 int i;
293
294 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000295 /* Signal is not automatically unmasked after it is raised,
296 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000297 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000298 /* pendingsig = 0; - now done in onsig() */
299
Denis Vlasenkob012b102007-02-19 22:43:01 +0000300 i = EXSIG;
301 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
302 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000303 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000304 signal(SIGINT, SIG_DFL);
305 raise(SIGINT);
306 }
307 i = EXINT;
308 }
309 raise_exception(i);
310 /* NOTREACHED */
311}
312
313#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000314static void
315int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000316{
317 if (--suppressint == 0 && intpending) {
318 raise_interrupt();
319 }
320}
321#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000322static void
323force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000324{
325 suppressint = 0;
326 if (intpending)
327 raise_interrupt();
328}
329#define FORCE_INT_ON force_int_on()
330#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000331#define INT_ON do { \
332 xbarrier(); \
333 if (--suppressint == 0 && intpending) \
334 raise_interrupt(); \
335} while (0)
336#define FORCE_INT_ON do { \
337 xbarrier(); \
338 suppressint = 0; \
339 if (intpending) \
340 raise_interrupt(); \
341} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000342#endif /* ASH_OPTIMIZE_FOR_SIZE */
343
344#define SAVE_INT(v) ((v) = suppressint)
345
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000346#define RESTORE_INT(v) do { \
347 xbarrier(); \
348 suppressint = (v); \
349 if (suppressint == 0 && intpending) \
350 raise_interrupt(); \
351} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000352
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000353/*
354 * Ignore a signal. Only one usage site - in forkchild()
355 */
356static void
357ignoresig(int signo)
358{
359 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
360 signal(signo, SIG_IGN);
361 }
362 sigmode[signo - 1] = S_HARD_IGN;
363}
364
365/*
366 * Signal handler. Only one usage site - in setsignal()
367 */
368static void
369onsig(int signo)
370{
371 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000372 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000373
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000374 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000375 if (!suppressint) {
376 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000377 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000378 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000379 intpending = 1;
380 }
381}
382
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000383
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000384/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000385
Eric Andersenc470f442003-07-28 09:56:35 +0000386static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000387outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000388{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000389 INT_OFF;
390 fputs(p, file);
391 INT_ON;
392}
393
394static void
395flush_stdout_stderr(void)
396{
397 INT_OFF;
398 fflush(stdout);
399 fflush(stderr);
400 INT_ON;
401}
402
403static void
404flush_stderr(void)
405{
406 INT_OFF;
407 fflush(stderr);
408 INT_ON;
409}
410
411static void
412outcslow(int c, FILE *dest)
413{
414 INT_OFF;
415 putc(c, dest);
416 fflush(dest);
417 INT_ON;
418}
419
420static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
421static int
422out1fmt(const char *fmt, ...)
423{
424 va_list ap;
425 int r;
426
427 INT_OFF;
428 va_start(ap, fmt);
429 r = vprintf(fmt, ap);
430 va_end(ap);
431 INT_ON;
432 return r;
433}
434
435static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
436static int
437fmtstr(char *outbuf, size_t length, const char *fmt, ...)
438{
439 va_list ap;
440 int ret;
441
442 va_start(ap, fmt);
443 INT_OFF;
444 ret = vsnprintf(outbuf, length, fmt, ap);
445 va_end(ap);
446 INT_ON;
447 return ret;
448}
449
450static void
451out1str(const char *p)
452{
453 outstr(p, stdout);
454}
455
456static void
457out2str(const char *p)
458{
459 outstr(p, stderr);
460 flush_stderr();
461}
462
463
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000464/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000465
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000466/* control characters in argument strings */
467#define CTLESC '\201' /* escape next character */
468#define CTLVAR '\202' /* variable defn */
469#define CTLENDVAR '\203'
470#define CTLBACKQ '\204'
471#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
472/* CTLBACKQ | CTLQUOTE == '\205' */
473#define CTLARI '\206' /* arithmetic expression */
474#define CTLENDARI '\207'
475#define CTLQUOTEMARK '\210'
476
477/* variable substitution byte (follows CTLVAR) */
478#define VSTYPE 0x0f /* type of variable substitution */
479#define VSNUL 0x10 /* colon--treat the empty string as unset */
480#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
481
482/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000483#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
484#define VSMINUS 0x2 /* ${var-text} */
485#define VSPLUS 0x3 /* ${var+text} */
486#define VSQUESTION 0x4 /* ${var?message} */
487#define VSASSIGN 0x5 /* ${var=text} */
488#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
489#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
490#define VSTRIMLEFT 0x8 /* ${var#pattern} */
491#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
492#define VSLENGTH 0xa /* ${#var} */
493#if ENABLE_ASH_BASH_COMPAT
494#define VSSUBSTR 0xc /* ${var:position:length} */
495#define VSREPLACE 0xd /* ${var/pattern/replacement} */
496#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
497#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000498
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000499static const char dolatstr[] ALIGN1 = {
500 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
501};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000502
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000503#define NCMD 0
504#define NPIPE 1
505#define NREDIR 2
506#define NBACKGND 3
507#define NSUBSHELL 4
508#define NAND 5
509#define NOR 6
510#define NSEMI 7
511#define NIF 8
512#define NWHILE 9
513#define NUNTIL 10
514#define NFOR 11
515#define NCASE 12
516#define NCLIST 13
517#define NDEFUN 14
518#define NARG 15
519#define NTO 16
520#define NCLOBBER 17
521#define NFROM 18
522#define NFROMTO 19
523#define NAPPEND 20
524#define NTOFD 21
525#define NFROMFD 22
526#define NHERE 23
527#define NXHERE 24
528#define NNOT 25
529
530union node;
531
532struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000533 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000534 union node *assign;
535 union node *args;
536 union node *redirect;
537};
538
539struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000540 smallint type;
541 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000542 struct nodelist *cmdlist;
543};
544
545struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000546 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000547 union node *n;
548 union node *redirect;
549};
550
551struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000552 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000553 union node *ch1;
554 union node *ch2;
555};
556
557struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000558 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000559 union node *test;
560 union node *ifpart;
561 union node *elsepart;
562};
563
564struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000565 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000566 union node *args;
567 union node *body;
568 char *var;
569};
570
571struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000572 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000573 union node *expr;
574 union node *cases;
575};
576
577struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000578 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000579 union node *next;
580 union node *pattern;
581 union node *body;
582};
583
584struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000585 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000586 union node *next;
587 char *text;
588 struct nodelist *backquote;
589};
590
591struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000592 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000593 union node *next;
594 int fd;
595 union node *fname;
596 char *expfname;
597};
598
599struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000600 smallint type;
Denis Vlasenko0b769642008-07-24 07:54:57 +0000601 union node *next; /* must match nfile's layout */
602 int fd; /* must match nfile's layout */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000603 int dupfd;
604 union node *vname;
605};
606
607struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000608 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000609 union node *next;
610 int fd;
611 union node *doc;
612};
613
614struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000615 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000616 union node *com;
617};
618
619union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000620 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621 struct ncmd ncmd;
622 struct npipe npipe;
623 struct nredir nredir;
624 struct nbinary nbinary;
625 struct nif nif;
626 struct nfor nfor;
627 struct ncase ncase;
628 struct nclist nclist;
629 struct narg narg;
630 struct nfile nfile;
631 struct ndup ndup;
632 struct nhere nhere;
633 struct nnot nnot;
634};
635
636struct nodelist {
637 struct nodelist *next;
638 union node *n;
639};
640
641struct funcnode {
642 int count;
643 union node n;
644};
645
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000646/*
647 * Free a parse tree.
648 */
649static void
650freefunc(struct funcnode *f)
651{
652 if (f && --f->count < 0)
653 free(f);
654}
655
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000656
657/* ============ Debugging output */
658
659#if DEBUG
660
661static FILE *tracefile;
662
663static void
664trace_printf(const char *fmt, ...)
665{
666 va_list va;
667
668 if (debug != 1)
669 return;
670 va_start(va, fmt);
671 vfprintf(tracefile, fmt, va);
672 va_end(va);
673}
674
675static void
676trace_vprintf(const char *fmt, va_list va)
677{
678 if (debug != 1)
679 return;
680 vfprintf(tracefile, fmt, va);
681}
682
683static void
684trace_puts(const char *s)
685{
686 if (debug != 1)
687 return;
688 fputs(s, tracefile);
689}
690
691static void
692trace_puts_quoted(char *s)
693{
694 char *p;
695 char c;
696
697 if (debug != 1)
698 return;
699 putc('"', tracefile);
700 for (p = s; *p; p++) {
701 switch (*p) {
702 case '\n': c = 'n'; goto backslash;
703 case '\t': c = 't'; goto backslash;
704 case '\r': c = 'r'; goto backslash;
705 case '"': c = '"'; goto backslash;
706 case '\\': c = '\\'; goto backslash;
707 case CTLESC: c = 'e'; goto backslash;
708 case CTLVAR: c = 'v'; goto backslash;
709 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
710 case CTLBACKQ: c = 'q'; goto backslash;
711 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
712 backslash:
713 putc('\\', tracefile);
714 putc(c, tracefile);
715 break;
716 default:
717 if (*p >= ' ' && *p <= '~')
718 putc(*p, tracefile);
719 else {
720 putc('\\', tracefile);
721 putc(*p >> 6 & 03, tracefile);
722 putc(*p >> 3 & 07, tracefile);
723 putc(*p & 07, tracefile);
724 }
725 break;
726 }
727 }
728 putc('"', tracefile);
729}
730
731static void
732trace_puts_args(char **ap)
733{
734 if (debug != 1)
735 return;
736 if (!*ap)
737 return;
738 while (1) {
739 trace_puts_quoted(*ap);
740 if (!*++ap) {
741 putc('\n', tracefile);
742 break;
743 }
744 putc(' ', tracefile);
745 }
746}
747
748static void
749opentrace(void)
750{
751 char s[100];
752#ifdef O_APPEND
753 int flags;
754#endif
755
756 if (debug != 1) {
757 if (tracefile)
758 fflush(tracefile);
759 /* leave open because libedit might be using it */
760 return;
761 }
762 strcpy(s, "./trace");
763 if (tracefile) {
764 if (!freopen(s, "a", tracefile)) {
765 fprintf(stderr, "Can't re-open %s\n", s);
766 debug = 0;
767 return;
768 }
769 } else {
770 tracefile = fopen(s, "a");
771 if (tracefile == NULL) {
772 fprintf(stderr, "Can't open %s\n", s);
773 debug = 0;
774 return;
775 }
776 }
777#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000778 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000779 if (flags >= 0)
780 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
781#endif
782 setlinebuf(tracefile);
783 fputs("\nTracing started.\n", tracefile);
784}
785
786static void
787indent(int amount, char *pfx, FILE *fp)
788{
789 int i;
790
791 for (i = 0; i < amount; i++) {
792 if (pfx && i == amount - 1)
793 fputs(pfx, fp);
794 putc('\t', fp);
795 }
796}
797
798/* little circular references here... */
799static void shtree(union node *n, int ind, char *pfx, FILE *fp);
800
801static void
802sharg(union node *arg, FILE *fp)
803{
804 char *p;
805 struct nodelist *bqlist;
806 int subtype;
807
808 if (arg->type != NARG) {
809 out1fmt("<node type %d>\n", arg->type);
810 abort();
811 }
812 bqlist = arg->narg.backquote;
813 for (p = arg->narg.text; *p; p++) {
814 switch (*p) {
815 case CTLESC:
816 putc(*++p, fp);
817 break;
818 case CTLVAR:
819 putc('$', fp);
820 putc('{', fp);
821 subtype = *++p;
822 if (subtype == VSLENGTH)
823 putc('#', fp);
824
825 while (*p != '=')
826 putc(*p++, fp);
827
828 if (subtype & VSNUL)
829 putc(':', fp);
830
831 switch (subtype & VSTYPE) {
832 case VSNORMAL:
833 putc('}', fp);
834 break;
835 case VSMINUS:
836 putc('-', fp);
837 break;
838 case VSPLUS:
839 putc('+', fp);
840 break;
841 case VSQUESTION:
842 putc('?', fp);
843 break;
844 case VSASSIGN:
845 putc('=', fp);
846 break;
847 case VSTRIMLEFT:
848 putc('#', fp);
849 break;
850 case VSTRIMLEFTMAX:
851 putc('#', fp);
852 putc('#', fp);
853 break;
854 case VSTRIMRIGHT:
855 putc('%', fp);
856 break;
857 case VSTRIMRIGHTMAX:
858 putc('%', fp);
859 putc('%', fp);
860 break;
861 case VSLENGTH:
862 break;
863 default:
864 out1fmt("<subtype %d>", subtype);
865 }
866 break;
867 case CTLENDVAR:
868 putc('}', fp);
869 break;
870 case CTLBACKQ:
871 case CTLBACKQ|CTLQUOTE:
872 putc('$', fp);
873 putc('(', fp);
874 shtree(bqlist->n, -1, NULL, fp);
875 putc(')', fp);
876 break;
877 default:
878 putc(*p, fp);
879 break;
880 }
881 }
882}
883
884static void
885shcmd(union node *cmd, FILE *fp)
886{
887 union node *np;
888 int first;
889 const char *s;
890 int dftfd;
891
892 first = 1;
893 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000894 if (!first)
895 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000896 sharg(np, fp);
897 first = 0;
898 }
899 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000900 if (!first)
901 putc(' ', fp);
902 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000903 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000904 case NTO: s = ">>"+1; dftfd = 1; break;
905 case NCLOBBER: s = ">|"; dftfd = 1; break;
906 case NAPPEND: s = ">>"; dftfd = 1; break;
907 case NTOFD: s = ">&"; dftfd = 1; break;
908 case NFROM: s = "<"; break;
909 case NFROMFD: s = "<&"; break;
910 case NFROMTO: s = "<>"; break;
911 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000912 }
913 if (np->nfile.fd != dftfd)
914 fprintf(fp, "%d", np->nfile.fd);
915 fputs(s, fp);
916 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
917 fprintf(fp, "%d", np->ndup.dupfd);
918 } else {
919 sharg(np->nfile.fname, fp);
920 }
921 first = 0;
922 }
923}
924
925static void
926shtree(union node *n, int ind, char *pfx, FILE *fp)
927{
928 struct nodelist *lp;
929 const char *s;
930
931 if (n == NULL)
932 return;
933
934 indent(ind, pfx, fp);
935 switch (n->type) {
936 case NSEMI:
937 s = "; ";
938 goto binop;
939 case NAND:
940 s = " && ";
941 goto binop;
942 case NOR:
943 s = " || ";
944 binop:
945 shtree(n->nbinary.ch1, ind, NULL, fp);
946 /* if (ind < 0) */
947 fputs(s, fp);
948 shtree(n->nbinary.ch2, ind, NULL, fp);
949 break;
950 case NCMD:
951 shcmd(n, fp);
952 if (ind >= 0)
953 putc('\n', fp);
954 break;
955 case NPIPE:
956 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
957 shcmd(lp->n, fp);
958 if (lp->next)
959 fputs(" | ", fp);
960 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000961 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000962 fputs(" &", fp);
963 if (ind >= 0)
964 putc('\n', fp);
965 break;
966 default:
967 fprintf(fp, "<node type %d>", n->type);
968 if (ind >= 0)
969 putc('\n', fp);
970 break;
971 }
972}
973
974static void
975showtree(union node *n)
976{
977 trace_puts("showtree called\n");
978 shtree(n, 1, NULL, stdout);
979}
980
981#define TRACE(param) trace_printf param
982#define TRACEV(param) trace_vprintf param
983
984#else
985
986#define TRACE(param)
987#define TRACEV(param)
988
989#endif /* DEBUG */
990
991
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000992/* ============ Parser data */
993
994/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000995 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
996 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000997struct strlist {
998 struct strlist *next;
999 char *text;
1000};
1001
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001002struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001003
Denis Vlasenkob012b102007-02-19 22:43:01 +00001004struct strpush {
1005 struct strpush *prev; /* preceding string on stack */
1006 char *prevstring;
1007 int prevnleft;
1008#if ENABLE_ASH_ALIAS
1009 struct alias *ap; /* if push was associated with an alias */
1010#endif
1011 char *string; /* remember the string since it may change */
1012};
1013
1014struct parsefile {
1015 struct parsefile *prev; /* preceding file on stack */
1016 int linno; /* current line */
1017 int fd; /* file descriptor (or -1 if string) */
1018 int nleft; /* number of chars left in this line */
1019 int lleft; /* number of chars left in this buffer */
1020 char *nextc; /* next char in buffer */
1021 char *buf; /* input buffer */
1022 struct strpush *strpush; /* for pushing strings at this level */
1023 struct strpush basestrpush; /* so pushing one is fast */
1024};
1025
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001026static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001027static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001028static int startlinno; /* line # where last token started */
1029static char *commandname; /* currently executing command */
1030static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001031static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001032
1033
1034/* ============ Message printing */
1035
1036static void
1037ash_vmsg(const char *msg, va_list ap)
1038{
1039 fprintf(stderr, "%s: ", arg0);
1040 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001041 if (strcmp(arg0, commandname))
1042 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001043 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001044 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001045 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001046 vfprintf(stderr, msg, ap);
1047 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001048}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001049
1050/*
1051 * Exverror is called to raise the error exception. If the second argument
1052 * is not NULL then error prints an error message using printf style
1053 * formatting. It then raises the error exception.
1054 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001055static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001056static void
1057ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001058{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001059#if DEBUG
1060 if (msg) {
1061 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1062 TRACEV((msg, ap));
1063 TRACE(("\") pid=%d\n", getpid()));
1064 } else
1065 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1066 if (msg)
1067#endif
1068 ash_vmsg(msg, ap);
1069
1070 flush_stdout_stderr();
1071 raise_exception(cond);
1072 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001073}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001074
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001075static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001076static void
1077ash_msg_and_raise_error(const char *msg, ...)
1078{
1079 va_list ap;
1080
1081 va_start(ap, msg);
1082 ash_vmsg_and_raise(EXERROR, msg, ap);
1083 /* NOTREACHED */
1084 va_end(ap);
1085}
1086
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001087static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001088static void
1089ash_msg_and_raise(int cond, const char *msg, ...)
1090{
1091 va_list ap;
1092
1093 va_start(ap, msg);
1094 ash_vmsg_and_raise(cond, msg, ap);
1095 /* NOTREACHED */
1096 va_end(ap);
1097}
1098
1099/*
1100 * error/warning routines for external builtins
1101 */
1102static void
1103ash_msg(const char *fmt, ...)
1104{
1105 va_list ap;
1106
1107 va_start(ap, fmt);
1108 ash_vmsg(fmt, ap);
1109 va_end(ap);
1110}
1111
1112/*
1113 * Return a string describing an error. The returned string may be a
1114 * pointer to a static buffer that will be overwritten on the next call.
1115 * Action describes the operation that got the error.
1116 */
1117static const char *
1118errmsg(int e, const char *em)
1119{
1120 if (e == ENOENT || e == ENOTDIR) {
1121 return em;
1122 }
1123 return strerror(e);
1124}
1125
1126
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001127/* ============ Memory allocation */
1128
1129/*
1130 * It appears that grabstackstr() will barf with such alignments
1131 * because stalloc() will return a string allocated in a new stackblock.
1132 */
1133#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1134enum {
1135 /* Most machines require the value returned from malloc to be aligned
1136 * in some way. The following macro will get this right
1137 * on many machines. */
1138 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1139 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001140 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001141};
1142
1143struct stack_block {
1144 struct stack_block *prev;
1145 char space[MINSIZE];
1146};
1147
1148struct stackmark {
1149 struct stack_block *stackp;
1150 char *stacknxt;
1151 size_t stacknleft;
1152 struct stackmark *marknext;
1153};
1154
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001155
Denis Vlasenko01631112007-12-16 17:20:38 +00001156struct globals_memstack {
1157 struct stack_block *g_stackp; // = &stackbase;
1158 struct stackmark *markp;
1159 char *g_stacknxt; // = stackbase.space;
1160 char *sstrend; // = stackbase.space + MINSIZE;
1161 size_t g_stacknleft; // = MINSIZE;
1162 int herefd; // = -1;
1163 struct stack_block stackbase;
1164};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001165extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1166#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001167#define g_stackp (G_memstack.g_stackp )
1168#define markp (G_memstack.markp )
1169#define g_stacknxt (G_memstack.g_stacknxt )
1170#define sstrend (G_memstack.sstrend )
1171#define g_stacknleft (G_memstack.g_stacknleft)
1172#define herefd (G_memstack.herefd )
1173#define stackbase (G_memstack.stackbase )
1174#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001175 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1176 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001177 g_stackp = &stackbase; \
1178 g_stacknxt = stackbase.space; \
1179 g_stacknleft = MINSIZE; \
1180 sstrend = stackbase.space + MINSIZE; \
1181 herefd = -1; \
1182} while (0)
1183
1184#define stackblock() ((void *)g_stacknxt)
1185#define stackblocksize() g_stacknleft
1186
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001187
1188static void *
1189ckrealloc(void * p, size_t nbytes)
1190{
1191 p = realloc(p, nbytes);
1192 if (!p)
1193 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1194 return p;
1195}
1196
1197static void *
1198ckmalloc(size_t nbytes)
1199{
1200 return ckrealloc(NULL, nbytes);
1201}
1202
Denis Vlasenko597906c2008-02-20 16:38:54 +00001203static void *
1204ckzalloc(size_t nbytes)
1205{
1206 return memset(ckmalloc(nbytes), 0, nbytes);
1207}
1208
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001209/*
1210 * Make a copy of a string in safe storage.
1211 */
1212static char *
1213ckstrdup(const char *s)
1214{
1215 char *p = strdup(s);
1216 if (!p)
1217 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1218 return p;
1219}
1220
1221/*
1222 * Parse trees for commands are allocated in lifo order, so we use a stack
1223 * to make this more efficient, and also to avoid all sorts of exception
1224 * handling code to handle interrupts in the middle of a parse.
1225 *
1226 * The size 504 was chosen because the Ultrix malloc handles that size
1227 * well.
1228 */
1229static void *
1230stalloc(size_t nbytes)
1231{
1232 char *p;
1233 size_t aligned;
1234
1235 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001236 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001237 size_t len;
1238 size_t blocksize;
1239 struct stack_block *sp;
1240
1241 blocksize = aligned;
1242 if (blocksize < MINSIZE)
1243 blocksize = MINSIZE;
1244 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1245 if (len < blocksize)
1246 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1247 INT_OFF;
1248 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001249 sp->prev = g_stackp;
1250 g_stacknxt = sp->space;
1251 g_stacknleft = blocksize;
1252 sstrend = g_stacknxt + blocksize;
1253 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001254 INT_ON;
1255 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001256 p = g_stacknxt;
1257 g_stacknxt += aligned;
1258 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001259 return p;
1260}
1261
Denis Vlasenko597906c2008-02-20 16:38:54 +00001262static void *
1263stzalloc(size_t nbytes)
1264{
1265 return memset(stalloc(nbytes), 0, nbytes);
1266}
1267
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001268static void
1269stunalloc(void *p)
1270{
1271#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001272 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001273 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001274 abort();
1275 }
1276#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001277 g_stacknleft += g_stacknxt - (char *)p;
1278 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001279}
1280
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001281/*
1282 * Like strdup but works with the ash stack.
1283 */
1284static char *
1285ststrdup(const char *p)
1286{
1287 size_t len = strlen(p) + 1;
1288 return memcpy(stalloc(len), p, len);
1289}
1290
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001291static void
1292setstackmark(struct stackmark *mark)
1293{
Denis Vlasenko01631112007-12-16 17:20:38 +00001294 mark->stackp = g_stackp;
1295 mark->stacknxt = g_stacknxt;
1296 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001297 mark->marknext = markp;
1298 markp = mark;
1299}
1300
1301static void
1302popstackmark(struct stackmark *mark)
1303{
1304 struct stack_block *sp;
1305
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001306 if (!mark->stackp)
1307 return;
1308
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001309 INT_OFF;
1310 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001311 while (g_stackp != mark->stackp) {
1312 sp = g_stackp;
1313 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001314 free(sp);
1315 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001316 g_stacknxt = mark->stacknxt;
1317 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001318 sstrend = mark->stacknxt + mark->stacknleft;
1319 INT_ON;
1320}
1321
1322/*
1323 * When the parser reads in a string, it wants to stick the string on the
1324 * stack and only adjust the stack pointer when it knows how big the
1325 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1326 * of space on top of the stack and stackblocklen returns the length of
1327 * this block. Growstackblock will grow this space by at least one byte,
1328 * possibly moving it (like realloc). Grabstackblock actually allocates the
1329 * part of the block that has been used.
1330 */
1331static void
1332growstackblock(void)
1333{
1334 size_t newlen;
1335
Denis Vlasenko01631112007-12-16 17:20:38 +00001336 newlen = g_stacknleft * 2;
1337 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001338 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1339 if (newlen < 128)
1340 newlen += 128;
1341
Denis Vlasenko01631112007-12-16 17:20:38 +00001342 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001343 struct stack_block *oldstackp;
1344 struct stackmark *xmark;
1345 struct stack_block *sp;
1346 struct stack_block *prevstackp;
1347 size_t grosslen;
1348
1349 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001350 oldstackp = g_stackp;
1351 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001352 prevstackp = sp->prev;
1353 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1354 sp = ckrealloc(sp, grosslen);
1355 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001356 g_stackp = sp;
1357 g_stacknxt = sp->space;
1358 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001359 sstrend = sp->space + newlen;
1360
1361 /*
1362 * Stack marks pointing to the start of the old block
1363 * must be relocated to point to the new block
1364 */
1365 xmark = markp;
1366 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001367 xmark->stackp = g_stackp;
1368 xmark->stacknxt = g_stacknxt;
1369 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001370 xmark = xmark->marknext;
1371 }
1372 INT_ON;
1373 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001374 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001375 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001376 char *p = stalloc(newlen);
1377
1378 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001379 g_stacknxt = memcpy(p, oldspace, oldlen);
1380 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001381 }
1382}
1383
1384static void
1385grabstackblock(size_t len)
1386{
1387 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001388 g_stacknxt += len;
1389 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001390}
1391
1392/*
1393 * The following routines are somewhat easier to use than the above.
1394 * The user declares a variable of type STACKSTR, which may be declared
1395 * to be a register. The macro STARTSTACKSTR initializes things. Then
1396 * the user uses the macro STPUTC to add characters to the string. In
1397 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1398 * grown as necessary. When the user is done, she can just leave the
1399 * string there and refer to it using stackblock(). Or she can allocate
1400 * the space for it using grabstackstr(). If it is necessary to allow
1401 * someone else to use the stack temporarily and then continue to grow
1402 * the string, the user should use grabstack to allocate the space, and
1403 * then call ungrabstr(p) to return to the previous mode of operation.
1404 *
1405 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1406 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1407 * is space for at least one character.
1408 */
1409static void *
1410growstackstr(void)
1411{
1412 size_t len = stackblocksize();
1413 if (herefd >= 0 && len >= 1024) {
1414 full_write(herefd, stackblock(), len);
1415 return stackblock();
1416 }
1417 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001418 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001419}
1420
1421/*
1422 * Called from CHECKSTRSPACE.
1423 */
1424static char *
1425makestrspace(size_t newlen, char *p)
1426{
Denis Vlasenko01631112007-12-16 17:20:38 +00001427 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001428 size_t size = stackblocksize();
1429
1430 for (;;) {
1431 size_t nleft;
1432
1433 size = stackblocksize();
1434 nleft = size - len;
1435 if (nleft >= newlen)
1436 break;
1437 growstackblock();
1438 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001439 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001440}
1441
1442static char *
1443stack_nputstr(const char *s, size_t n, char *p)
1444{
1445 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001446 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001447 return p;
1448}
1449
1450static char *
1451stack_putstr(const char *s, char *p)
1452{
1453 return stack_nputstr(s, strlen(s), p);
1454}
1455
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001456static char *
1457_STPUTC(int c, char *p)
1458{
1459 if (p == sstrend)
1460 p = growstackstr();
1461 *p++ = c;
1462 return p;
1463}
1464
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001465#define STARTSTACKSTR(p) ((p) = stackblock())
1466#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001467#define CHECKSTRSPACE(n, p) do { \
1468 char *q = (p); \
1469 size_t l = (n); \
1470 size_t m = sstrend - q; \
1471 if (l > m) \
1472 (p) = makestrspace(l, q); \
1473} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001474#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001475#define STACKSTRNUL(p) do { \
1476 if ((p) == sstrend) \
1477 (p) = growstackstr(); \
1478 *(p) = '\0'; \
1479} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001480#define STUNPUTC(p) (--(p))
1481#define STTOPC(p) ((p)[-1])
1482#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001483
1484#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001485#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001486#define stackstrend() ((void *)sstrend)
1487
1488
1489/* ============ String helpers */
1490
1491/*
1492 * prefix -- see if pfx is a prefix of string.
1493 */
1494static char *
1495prefix(const char *string, const char *pfx)
1496{
1497 while (*pfx) {
1498 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001499 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001500 }
1501 return (char *) string;
1502}
1503
1504/*
1505 * Check for a valid number. This should be elsewhere.
1506 */
1507static int
1508is_number(const char *p)
1509{
1510 do {
1511 if (!isdigit(*p))
1512 return 0;
1513 } while (*++p != '\0');
1514 return 1;
1515}
1516
1517/*
1518 * Convert a string of digits to an integer, printing an error message on
1519 * failure.
1520 */
1521static int
1522number(const char *s)
1523{
1524 if (!is_number(s))
1525 ash_msg_and_raise_error(illnum, s);
1526 return atoi(s);
1527}
1528
1529/*
1530 * Produce a possibly single quoted string suitable as input to the shell.
1531 * The return string is allocated on the stack.
1532 */
1533static char *
1534single_quote(const char *s)
1535{
1536 char *p;
1537
1538 STARTSTACKSTR(p);
1539
1540 do {
1541 char *q;
1542 size_t len;
1543
1544 len = strchrnul(s, '\'') - s;
1545
1546 q = p = makestrspace(len + 3, p);
1547
1548 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001549 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001550 *q++ = '\'';
1551 s += len;
1552
1553 STADJUST(q - p, p);
1554
1555 len = strspn(s, "'");
1556 if (!len)
1557 break;
1558
1559 q = p = makestrspace(len + 3, p);
1560
1561 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001562 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001563 *q++ = '"';
1564 s += len;
1565
1566 STADJUST(q - p, p);
1567 } while (*s);
1568
1569 USTPUTC(0, p);
1570
1571 return stackblock();
1572}
1573
1574
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001575/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001576
1577static char **argptr; /* argument list for builtin commands */
1578static char *optionarg; /* set by nextopt (like getopt) */
1579static char *optptr; /* used by nextopt */
1580
1581/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001582 * XXX - should get rid of. Have all builtins use getopt(3).
1583 * The library getopt must have the BSD extension static variable
1584 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001585 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001586 * Standard option processing (a la getopt) for builtin routines.
1587 * The only argument that is passed to nextopt is the option string;
1588 * the other arguments are unnecessary. It returns the character,
1589 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001590 */
1591static int
1592nextopt(const char *optstring)
1593{
1594 char *p;
1595 const char *q;
1596 char c;
1597
1598 p = optptr;
1599 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001600 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001601 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001602 if (p == NULL)
1603 return '\0';
1604 if (*p != '-')
1605 return '\0';
1606 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001607 return '\0';
1608 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001609 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001610 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001611 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001612 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001613 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001614 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001615 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001616 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001617 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001618 if (*++q == ':')
1619 q++;
1620 }
1621 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001622 if (*p == '\0') {
1623 p = *argptr++;
1624 if (p == NULL)
1625 ash_msg_and_raise_error("no arg for -%c option", c);
1626 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001627 optionarg = p;
1628 p = NULL;
1629 }
1630 optptr = p;
1631 return c;
1632}
1633
1634
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001635/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001636
Denis Vlasenko01631112007-12-16 17:20:38 +00001637/*
1638 * The parsefile structure pointed to by the global variable parsefile
1639 * contains information about the current file being read.
1640 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001641struct shparam {
1642 int nparam; /* # of positional parameters (without $0) */
1643#if ENABLE_ASH_GETOPTS
1644 int optind; /* next parameter to be processed by getopts */
1645 int optoff; /* used by getopts */
1646#endif
1647 unsigned char malloced; /* if parameter list dynamically allocated */
1648 char **p; /* parameter list */
1649};
1650
1651/*
1652 * Free the list of positional parameters.
1653 */
1654static void
1655freeparam(volatile struct shparam *param)
1656{
Denis Vlasenko01631112007-12-16 17:20:38 +00001657 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001658 char **ap, **ap1;
1659 ap = ap1 = param->p;
1660 while (*ap)
1661 free(*ap++);
1662 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001663 }
1664}
1665
1666#if ENABLE_ASH_GETOPTS
1667static void getoptsreset(const char *value);
1668#endif
1669
1670struct var {
1671 struct var *next; /* next entry in hash list */
1672 int flags; /* flags are defined above */
1673 const char *text; /* name=value */
1674 void (*func)(const char *); /* function to be called when */
1675 /* the variable gets set/unset */
1676};
1677
1678struct localvar {
1679 struct localvar *next; /* next local variable in list */
1680 struct var *vp; /* the variable that was made local */
1681 int flags; /* saved flags */
1682 const char *text; /* saved text */
1683};
1684
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001685/* flags */
1686#define VEXPORT 0x01 /* variable is exported */
1687#define VREADONLY 0x02 /* variable cannot be modified */
1688#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1689#define VTEXTFIXED 0x08 /* text is statically allocated */
1690#define VSTACK 0x10 /* text is allocated on the stack */
1691#define VUNSET 0x20 /* the variable is not set */
1692#define VNOFUNC 0x40 /* don't call the callback function */
1693#define VNOSET 0x80 /* do not set variable - just readonly test */
1694#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001695#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001696# define VDYNAMIC 0x200 /* dynamic variable */
1697#else
1698# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001699#endif
1700
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001701#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001702static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001703#define defifs (defifsvar + 4)
1704#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001705static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001706#endif
1707
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001708
Denis Vlasenko01631112007-12-16 17:20:38 +00001709/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001710#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001711static void
1712change_lc_all(const char *value)
1713{
1714 if (value && *value != '\0')
1715 setlocale(LC_ALL, value);
1716}
1717static void
1718change_lc_ctype(const char *value)
1719{
1720 if (value && *value != '\0')
1721 setlocale(LC_CTYPE, value);
1722}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001723#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001724#if ENABLE_ASH_MAIL
1725static void chkmail(void);
1726static void changemail(const char *);
1727#endif
1728static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729#if ENABLE_ASH_RANDOM_SUPPORT
1730static void change_random(const char *);
1731#endif
1732
Denis Vlasenko01631112007-12-16 17:20:38 +00001733static const struct {
1734 int flags;
1735 const char *text;
1736 void (*func)(const char *);
1737} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001739 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001741 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001744 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1745 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001747 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1748 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1749 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1750 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001752 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#endif
1754#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001755 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001756#endif
1757#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001758 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1759 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760#endif
1761#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001762 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001763#endif
1764};
1765
Denis Vlasenko0b769642008-07-24 07:54:57 +00001766struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001767
1768struct globals_var {
1769 struct shparam shellparam; /* $@ current positional parameters */
1770 struct redirtab *redirlist;
1771 int g_nullredirs;
1772 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1773 struct var *vartab[VTABSIZE];
1774 struct var varinit[ARRAY_SIZE(varinit_data)];
1775};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001776extern struct globals_var *const ash_ptr_to_globals_var;
1777#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001778#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001779//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001780#define g_nullredirs (G_var.g_nullredirs )
1781#define preverrout_fd (G_var.preverrout_fd)
1782#define vartab (G_var.vartab )
1783#define varinit (G_var.varinit )
1784#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001785 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001786 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1787 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001788 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1789 varinit[i].flags = varinit_data[i].flags; \
1790 varinit[i].text = varinit_data[i].text; \
1791 varinit[i].func = varinit_data[i].func; \
1792 } \
1793} while (0)
1794
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001795#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001796#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001797# define vmail (&vifs)[1]
1798# define vmpath (&vmail)[1]
1799# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001800#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001801# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001802#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001803#define vps1 (&vpath)[1]
1804#define vps2 (&vps1)[1]
1805#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001806#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001807# define voptind (&vps4)[1]
1808# if ENABLE_ASH_RANDOM_SUPPORT
1809# define vrandom (&voptind)[1]
1810# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001811#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001812# if ENABLE_ASH_RANDOM_SUPPORT
1813# define vrandom (&vps4)[1]
1814# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001815#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001816
1817/*
1818 * The following macros access the values of the above variables.
1819 * They have to skip over the name. They return the null string
1820 * for unset variables.
1821 */
1822#define ifsval() (vifs.text + 4)
1823#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001824#if ENABLE_ASH_MAIL
1825# define mailval() (vmail.text + 5)
1826# define mpathval() (vmpath.text + 9)
1827# define mpathset() ((vmpath.flags & VUNSET) == 0)
1828#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001829#define pathval() (vpath.text + 5)
1830#define ps1val() (vps1.text + 4)
1831#define ps2val() (vps2.text + 4)
1832#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001833#if ENABLE_ASH_GETOPTS
1834# define optindval() (voptind.text + 7)
1835#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001836
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001837
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001838#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1839#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1840
Denis Vlasenko01631112007-12-16 17:20:38 +00001841#if ENABLE_ASH_GETOPTS
1842static void
1843getoptsreset(const char *value)
1844{
1845 shellparam.optind = number(value);
1846 shellparam.optoff = -1;
1847}
1848#endif
1849
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001850/*
1851 * Return of a legal variable name (a letter or underscore followed by zero or
1852 * more letters, underscores, and digits).
1853 */
1854static char *
1855endofname(const char *name)
1856{
1857 char *p;
1858
1859 p = (char *) name;
1860 if (!is_name(*p))
1861 return p;
1862 while (*++p) {
1863 if (!is_in_name(*p))
1864 break;
1865 }
1866 return p;
1867}
1868
1869/*
1870 * Compares two strings up to the first = or '\0'. The first
1871 * string must be terminated by '='; the second may be terminated by
1872 * either '=' or '\0'.
1873 */
1874static int
1875varcmp(const char *p, const char *q)
1876{
1877 int c, d;
1878
1879 while ((c = *p) == (d = *q)) {
1880 if (!c || c == '=')
1881 goto out;
1882 p++;
1883 q++;
1884 }
1885 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001886 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001887 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001888 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001889 out:
1890 return c - d;
1891}
1892
1893static int
1894varequal(const char *a, const char *b)
1895{
1896 return !varcmp(a, b);
1897}
1898
1899/*
1900 * Find the appropriate entry in the hash table from the name.
1901 */
1902static struct var **
1903hashvar(const char *p)
1904{
1905 unsigned hashval;
1906
1907 hashval = ((unsigned char) *p) << 4;
1908 while (*p && *p != '=')
1909 hashval += (unsigned char) *p++;
1910 return &vartab[hashval % VTABSIZE];
1911}
1912
1913static int
1914vpcmp(const void *a, const void *b)
1915{
1916 return varcmp(*(const char **)a, *(const char **)b);
1917}
1918
1919/*
1920 * This routine initializes the builtin variables.
1921 */
1922static void
1923initvar(void)
1924{
1925 struct var *vp;
1926 struct var *end;
1927 struct var **vpp;
1928
1929 /*
1930 * PS1 depends on uid
1931 */
1932#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1933 vps1.text = "PS1=\\w \\$ ";
1934#else
1935 if (!geteuid())
1936 vps1.text = "PS1=# ";
1937#endif
1938 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001939 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001940 do {
1941 vpp = hashvar(vp->text);
1942 vp->next = *vpp;
1943 *vpp = vp;
1944 } while (++vp < end);
1945}
1946
1947static struct var **
1948findvar(struct var **vpp, const char *name)
1949{
1950 for (; *vpp; vpp = &(*vpp)->next) {
1951 if (varequal((*vpp)->text, name)) {
1952 break;
1953 }
1954 }
1955 return vpp;
1956}
1957
1958/*
1959 * Find the value of a variable. Returns NULL if not set.
1960 */
1961static char *
1962lookupvar(const char *name)
1963{
1964 struct var *v;
1965
1966 v = *findvar(hashvar(name), name);
1967 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001968#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001969 /*
1970 * Dynamic variables are implemented roughly the same way they are
1971 * in bash. Namely, they're "special" so long as they aren't unset.
1972 * As soon as they're unset, they're no longer dynamic, and dynamic
1973 * lookup will no longer happen at that point. -- PFM.
1974 */
1975 if ((v->flags & VDYNAMIC))
1976 (*v->func)(NULL);
1977#endif
1978 if (!(v->flags & VUNSET))
1979 return strchrnul(v->text, '=') + 1;
1980 }
1981 return NULL;
1982}
1983
1984/*
1985 * Search the environment of a builtin command.
1986 */
1987static char *
1988bltinlookup(const char *name)
1989{
1990 struct strlist *sp;
1991
1992 for (sp = cmdenviron; sp; sp = sp->next) {
1993 if (varequal(sp->text, name))
1994 return strchrnul(sp->text, '=') + 1;
1995 }
1996 return lookupvar(name);
1997}
1998
1999/*
2000 * Same as setvar except that the variable and value are passed in
2001 * the first argument as name=value. Since the first argument will
2002 * be actually stored in the table, it should not be a string that
2003 * will go away.
2004 * Called with interrupts off.
2005 */
2006static void
2007setvareq(char *s, int flags)
2008{
2009 struct var *vp, **vpp;
2010
2011 vpp = hashvar(s);
2012 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2013 vp = *findvar(vpp, s);
2014 if (vp) {
2015 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2016 const char *n;
2017
2018 if (flags & VNOSAVE)
2019 free(s);
2020 n = vp->text;
2021 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2022 }
2023
2024 if (flags & VNOSET)
2025 return;
2026
2027 if (vp->func && (flags & VNOFUNC) == 0)
2028 (*vp->func)(strchrnul(s, '=') + 1);
2029
2030 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2031 free((char*)vp->text);
2032
2033 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2034 } else {
2035 if (flags & VNOSET)
2036 return;
2037 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002038 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002039 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002040 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002041 *vpp = vp;
2042 }
2043 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2044 s = ckstrdup(s);
2045 vp->text = s;
2046 vp->flags = flags;
2047}
2048
2049/*
2050 * Set the value of a variable. The flags argument is ored with the
2051 * flags of the variable. If val is NULL, the variable is unset.
2052 */
2053static void
2054setvar(const char *name, const char *val, int flags)
2055{
2056 char *p, *q;
2057 size_t namelen;
2058 char *nameeq;
2059 size_t vallen;
2060
2061 q = endofname(name);
2062 p = strchrnul(q, '=');
2063 namelen = p - name;
2064 if (!namelen || p != q)
2065 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2066 vallen = 0;
2067 if (val == NULL) {
2068 flags |= VUNSET;
2069 } else {
2070 vallen = strlen(val);
2071 }
2072 INT_OFF;
2073 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002074 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002075 if (val) {
2076 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002077 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002078 }
2079 *p = '\0';
2080 setvareq(nameeq, flags | VNOSAVE);
2081 INT_ON;
2082}
2083
2084#if ENABLE_ASH_GETOPTS
2085/*
2086 * Safe version of setvar, returns 1 on success 0 on failure.
2087 */
2088static int
2089setvarsafe(const char *name, const char *val, int flags)
2090{
2091 int err;
2092 volatile int saveint;
2093 struct jmploc *volatile savehandler = exception_handler;
2094 struct jmploc jmploc;
2095
2096 SAVE_INT(saveint);
2097 if (setjmp(jmploc.loc))
2098 err = 1;
2099 else {
2100 exception_handler = &jmploc;
2101 setvar(name, val, flags);
2102 err = 0;
2103 }
2104 exception_handler = savehandler;
2105 RESTORE_INT(saveint);
2106 return err;
2107}
2108#endif
2109
2110/*
2111 * Unset the specified variable.
2112 */
2113static int
2114unsetvar(const char *s)
2115{
2116 struct var **vpp;
2117 struct var *vp;
2118 int retval;
2119
2120 vpp = findvar(hashvar(s), s);
2121 vp = *vpp;
2122 retval = 2;
2123 if (vp) {
2124 int flags = vp->flags;
2125
2126 retval = 1;
2127 if (flags & VREADONLY)
2128 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002129#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002130 vp->flags &= ~VDYNAMIC;
2131#endif
2132 if (flags & VUNSET)
2133 goto ok;
2134 if ((flags & VSTRFIXED) == 0) {
2135 INT_OFF;
2136 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2137 free((char*)vp->text);
2138 *vpp = vp->next;
2139 free(vp);
2140 INT_ON;
2141 } else {
2142 setvar(s, 0, 0);
2143 vp->flags &= ~VEXPORT;
2144 }
2145 ok:
2146 retval = 0;
2147 }
2148 out:
2149 return retval;
2150}
2151
2152/*
2153 * Process a linked list of variable assignments.
2154 */
2155static void
2156listsetvar(struct strlist *list_set_var, int flags)
2157{
2158 struct strlist *lp = list_set_var;
2159
2160 if (!lp)
2161 return;
2162 INT_OFF;
2163 do {
2164 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002165 lp = lp->next;
2166 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002167 INT_ON;
2168}
2169
2170/*
2171 * Generate a list of variables satisfying the given conditions.
2172 */
2173static char **
2174listvars(int on, int off, char ***end)
2175{
2176 struct var **vpp;
2177 struct var *vp;
2178 char **ep;
2179 int mask;
2180
2181 STARTSTACKSTR(ep);
2182 vpp = vartab;
2183 mask = on | off;
2184 do {
2185 for (vp = *vpp; vp; vp = vp->next) {
2186 if ((vp->flags & mask) == on) {
2187 if (ep == stackstrend())
2188 ep = growstackstr();
2189 *ep++ = (char *) vp->text;
2190 }
2191 }
2192 } while (++vpp < vartab + VTABSIZE);
2193 if (ep == stackstrend())
2194 ep = growstackstr();
2195 if (end)
2196 *end = ep;
2197 *ep++ = NULL;
2198 return grabstackstr(ep);
2199}
2200
2201
2202/* ============ Path search helper
2203 *
2204 * The variable path (passed by reference) should be set to the start
2205 * of the path before the first call; padvance will update
2206 * this value as it proceeds. Successive calls to padvance will return
2207 * the possible path expansions in sequence. If an option (indicated by
2208 * a percent sign) appears in the path entry then the global variable
2209 * pathopt will be set to point to it; otherwise pathopt will be set to
2210 * NULL.
2211 */
2212static const char *pathopt; /* set by padvance */
2213
2214static char *
2215padvance(const char **path, const char *name)
2216{
2217 const char *p;
2218 char *q;
2219 const char *start;
2220 size_t len;
2221
2222 if (*path == NULL)
2223 return NULL;
2224 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002225 for (p = start; *p && *p != ':' && *p != '%'; p++)
2226 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002227 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2228 while (stackblocksize() < len)
2229 growstackblock();
2230 q = stackblock();
2231 if (p != start) {
2232 memcpy(q, start, p - start);
2233 q += p - start;
2234 *q++ = '/';
2235 }
2236 strcpy(q, name);
2237 pathopt = NULL;
2238 if (*p == '%') {
2239 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002240 while (*p && *p != ':')
2241 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002242 }
2243 if (*p == ':')
2244 *path = p + 1;
2245 else
2246 *path = NULL;
2247 return stalloc(len);
2248}
2249
2250
2251/* ============ Prompt */
2252
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002253static smallint doprompt; /* if set, prompt the user */
2254static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002255
2256#if ENABLE_FEATURE_EDITING
2257static line_input_t *line_input_state;
2258static const char *cmdedit_prompt;
2259static void
2260putprompt(const char *s)
2261{
2262 if (ENABLE_ASH_EXPAND_PRMT) {
2263 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002264 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002265 return;
2266 }
2267 cmdedit_prompt = s;
2268}
2269#else
2270static void
2271putprompt(const char *s)
2272{
2273 out2str(s);
2274}
2275#endif
2276
2277#if ENABLE_ASH_EXPAND_PRMT
2278/* expandstr() needs parsing machinery, so it is far away ahead... */
2279static const char *expandstr(const char *ps);
2280#else
2281#define expandstr(s) s
2282#endif
2283
2284static void
2285setprompt(int whichprompt)
2286{
2287 const char *prompt;
2288#if ENABLE_ASH_EXPAND_PRMT
2289 struct stackmark smark;
2290#endif
2291
2292 needprompt = 0;
2293
2294 switch (whichprompt) {
2295 case 1:
2296 prompt = ps1val();
2297 break;
2298 case 2:
2299 prompt = ps2val();
2300 break;
2301 default: /* 0 */
2302 prompt = nullstr;
2303 }
2304#if ENABLE_ASH_EXPAND_PRMT
2305 setstackmark(&smark);
2306 stalloc(stackblocksize());
2307#endif
2308 putprompt(expandstr(prompt));
2309#if ENABLE_ASH_EXPAND_PRMT
2310 popstackmark(&smark);
2311#endif
2312}
2313
2314
2315/* ============ The cd and pwd commands */
2316
2317#define CD_PHYSICAL 1
2318#define CD_PRINT 2
2319
2320static int docd(const char *, int);
2321
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002322static int
2323cdopt(void)
2324{
2325 int flags = 0;
2326 int i, j;
2327
2328 j = 'L';
2329 while ((i = nextopt("LP"))) {
2330 if (i != j) {
2331 flags ^= CD_PHYSICAL;
2332 j = i;
2333 }
2334 }
2335
2336 return flags;
2337}
2338
2339/*
2340 * Update curdir (the name of the current directory) in response to a
2341 * cd command.
2342 */
2343static const char *
2344updatepwd(const char *dir)
2345{
2346 char *new;
2347 char *p;
2348 char *cdcomppath;
2349 const char *lim;
2350
2351 cdcomppath = ststrdup(dir);
2352 STARTSTACKSTR(new);
2353 if (*dir != '/') {
2354 if (curdir == nullstr)
2355 return 0;
2356 new = stack_putstr(curdir, new);
2357 }
2358 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002359 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002360 if (*dir != '/') {
2361 if (new[-1] != '/')
2362 USTPUTC('/', new);
2363 if (new > lim && *lim == '/')
2364 lim++;
2365 } else {
2366 USTPUTC('/', new);
2367 cdcomppath++;
2368 if (dir[1] == '/' && dir[2] != '/') {
2369 USTPUTC('/', new);
2370 cdcomppath++;
2371 lim++;
2372 }
2373 }
2374 p = strtok(cdcomppath, "/");
2375 while (p) {
2376 switch (*p) {
2377 case '.':
2378 if (p[1] == '.' && p[2] == '\0') {
2379 while (new > lim) {
2380 STUNPUTC(new);
2381 if (new[-1] == '/')
2382 break;
2383 }
2384 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002385 }
2386 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002387 break;
2388 /* fall through */
2389 default:
2390 new = stack_putstr(p, new);
2391 USTPUTC('/', new);
2392 }
2393 p = strtok(0, "/");
2394 }
2395 if (new > lim)
2396 STUNPUTC(new);
2397 *new = 0;
2398 return stackblock();
2399}
2400
2401/*
2402 * Find out what the current directory is. If we already know the current
2403 * directory, this routine returns immediately.
2404 */
2405static char *
2406getpwd(void)
2407{
Denis Vlasenko01631112007-12-16 17:20:38 +00002408 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002409 return dir ? dir : nullstr;
2410}
2411
2412static void
2413setpwd(const char *val, int setold)
2414{
2415 char *oldcur, *dir;
2416
2417 oldcur = dir = curdir;
2418
2419 if (setold) {
2420 setvar("OLDPWD", oldcur, VEXPORT);
2421 }
2422 INT_OFF;
2423 if (physdir != nullstr) {
2424 if (physdir != oldcur)
2425 free(physdir);
2426 physdir = nullstr;
2427 }
2428 if (oldcur == val || !val) {
2429 char *s = getpwd();
2430 physdir = s;
2431 if (!val)
2432 dir = s;
2433 } else
2434 dir = ckstrdup(val);
2435 if (oldcur != dir && oldcur != nullstr) {
2436 free(oldcur);
2437 }
2438 curdir = dir;
2439 INT_ON;
2440 setvar("PWD", dir, VEXPORT);
2441}
2442
2443static void hashcd(void);
2444
2445/*
2446 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2447 * know that the current directory has changed.
2448 */
2449static int
2450docd(const char *dest, int flags)
2451{
2452 const char *dir = 0;
2453 int err;
2454
2455 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2456
2457 INT_OFF;
2458 if (!(flags & CD_PHYSICAL)) {
2459 dir = updatepwd(dest);
2460 if (dir)
2461 dest = dir;
2462 }
2463 err = chdir(dest);
2464 if (err)
2465 goto out;
2466 setpwd(dir, 1);
2467 hashcd();
2468 out:
2469 INT_ON;
2470 return err;
2471}
2472
2473static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002474cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002475{
2476 const char *dest;
2477 const char *path;
2478 const char *p;
2479 char c;
2480 struct stat statb;
2481 int flags;
2482
2483 flags = cdopt();
2484 dest = *argptr;
2485 if (!dest)
2486 dest = bltinlookup(homestr);
2487 else if (LONE_DASH(dest)) {
2488 dest = bltinlookup("OLDPWD");
2489 flags |= CD_PRINT;
2490 }
2491 if (!dest)
2492 dest = nullstr;
2493 if (*dest == '/')
2494 goto step7;
2495 if (*dest == '.') {
2496 c = dest[1];
2497 dotdot:
2498 switch (c) {
2499 case '\0':
2500 case '/':
2501 goto step6;
2502 case '.':
2503 c = dest[2];
2504 if (c != '.')
2505 goto dotdot;
2506 }
2507 }
2508 if (!*dest)
2509 dest = ".";
2510 path = bltinlookup("CDPATH");
2511 if (!path) {
2512 step6:
2513 step7:
2514 p = dest;
2515 goto docd;
2516 }
2517 do {
2518 c = *path;
2519 p = padvance(&path, dest);
2520 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2521 if (c && c != ':')
2522 flags |= CD_PRINT;
2523 docd:
2524 if (!docd(p, flags))
2525 goto out;
2526 break;
2527 }
2528 } while (path);
2529 ash_msg_and_raise_error("can't cd to %s", dest);
2530 /* NOTREACHED */
2531 out:
2532 if (flags & CD_PRINT)
2533 out1fmt(snlfmt, curdir);
2534 return 0;
2535}
2536
2537static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002538pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002539{
2540 int flags;
2541 const char *dir = curdir;
2542
2543 flags = cdopt();
2544 if (flags) {
2545 if (physdir == nullstr)
2546 setpwd(dir, 0);
2547 dir = physdir;
2548 }
2549 out1fmt(snlfmt, dir);
2550 return 0;
2551}
2552
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002553
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002554/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002555
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002556#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002557#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002558
Eric Andersenc470f442003-07-28 09:56:35 +00002559/* Syntax classes */
2560#define CWORD 0 /* character is nothing special */
2561#define CNL 1 /* newline character */
2562#define CBACK 2 /* a backslash character */
2563#define CSQUOTE 3 /* single quote */
2564#define CDQUOTE 4 /* double quote */
2565#define CENDQUOTE 5 /* a terminating quote */
2566#define CBQUOTE 6 /* backwards single quote */
2567#define CVAR 7 /* a dollar sign */
2568#define CENDVAR 8 /* a '}' character */
2569#define CLP 9 /* a left paren in arithmetic */
2570#define CRP 10 /* a right paren in arithmetic */
2571#define CENDFILE 11 /* end of file */
2572#define CCTL 12 /* like CWORD, except it must be escaped */
2573#define CSPCL 13 /* these terminate a word */
2574#define CIGN 14 /* character should be ignored */
2575
Denis Vlasenko131ae172007-02-18 13:00:19 +00002576#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002577#define SYNBASE 130
2578#define PEOF -130
2579#define PEOA -129
2580#define PEOA_OR_PEOF PEOA
2581#else
2582#define SYNBASE 129
2583#define PEOF -129
2584#define PEOA_OR_PEOF PEOF
2585#endif
2586
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002587/* number syntax index */
2588#define BASESYNTAX 0 /* not in quotes */
2589#define DQSYNTAX 1 /* in double quotes */
2590#define SQSYNTAX 2 /* in single quotes */
2591#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002592#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002593
Denis Vlasenko131ae172007-02-18 13:00:19 +00002594#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002595#define USE_SIT_FUNCTION
2596#endif
2597
Denis Vlasenko131ae172007-02-18 13:00:19 +00002598#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002599static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002600#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002601 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002602#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002603 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2604 { CNL, CNL, CNL, CNL }, /* 2, \n */
2605 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2606 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2607 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2608 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2609 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2610 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2611 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2612 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2613 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002614#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002615 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2616 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2617 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002618#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002619};
Eric Andersenc470f442003-07-28 09:56:35 +00002620#else
2621static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002622#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002623 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002624#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002625 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2626 { CNL, CNL, CNL }, /* 2, \n */
2627 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2628 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2629 { CVAR, CVAR, CWORD }, /* 5, $ */
2630 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2631 { CSPCL, CWORD, CWORD }, /* 7, ( */
2632 { CSPCL, CWORD, CWORD }, /* 8, ) */
2633 { CBACK, CBACK, CCTL }, /* 9, \ */
2634 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2635 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002636#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002637 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2638 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2639 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002640#endif
2641};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002642#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002643
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002644#ifdef USE_SIT_FUNCTION
2645
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002646static int
2647SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002648{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002649 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002650#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002651 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002652 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2653 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2654 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2655 11, 3 /* "}~" */
2656 };
2657#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002658 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002659 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2660 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2661 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2662 10, 2 /* "}~" */
2663 };
2664#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002665 const char *s;
2666 int indx;
2667
Eric Andersenc470f442003-07-28 09:56:35 +00002668 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002669 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002670#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002671 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002672 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002673 else
2674#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002675#define U_C(c) ((unsigned char)(c))
2676
2677 if ((unsigned char)c >= (unsigned char)(CTLESC)
2678 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2679 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002680 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002681 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002682 s = strchrnul(spec_symbls, c);
2683 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002684 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002685 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002686 }
2687 return S_I_T[indx][syntax];
2688}
2689
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002690#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002691
Denis Vlasenko131ae172007-02-18 13:00:19 +00002692#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002693#define CSPCL_CIGN_CIGN_CIGN 0
2694#define CSPCL_CWORD_CWORD_CWORD 1
2695#define CNL_CNL_CNL_CNL 2
2696#define CWORD_CCTL_CCTL_CWORD 3
2697#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2698#define CVAR_CVAR_CWORD_CVAR 5
2699#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2700#define CSPCL_CWORD_CWORD_CLP 7
2701#define CSPCL_CWORD_CWORD_CRP 8
2702#define CBACK_CBACK_CCTL_CBACK 9
2703#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2704#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2705#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2706#define CWORD_CWORD_CWORD_CWORD 13
2707#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002708#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002709#define CSPCL_CWORD_CWORD_CWORD 0
2710#define CNL_CNL_CNL_CNL 1
2711#define CWORD_CCTL_CCTL_CWORD 2
2712#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2713#define CVAR_CVAR_CWORD_CVAR 4
2714#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2715#define CSPCL_CWORD_CWORD_CLP 6
2716#define CSPCL_CWORD_CWORD_CRP 7
2717#define CBACK_CBACK_CCTL_CBACK 8
2718#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2719#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2720#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2721#define CWORD_CWORD_CWORD_CWORD 12
2722#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002723#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002724
2725static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002726 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002727 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002728#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002729 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2730#endif
2731 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2733 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2734 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2735 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2736 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2737 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2738 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2739 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002740 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2869 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2870 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2892 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002893 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002894 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2896 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002898 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002899 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2900 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2901 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2902 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2904 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2905 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2906 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2907 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2918 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2919 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2920 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2921 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2922 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2923 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2951 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2952 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2953 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2956 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2984 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2985 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2986 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002987};
2988
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002989#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2990
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002991#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002992
Eric Andersen2870d962001-07-02 17:27:21 +00002993
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002994/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002995
Denis Vlasenko131ae172007-02-18 13:00:19 +00002996#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002997
2998#define ALIASINUSE 1
2999#define ALIASDEAD 2
3000
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003001struct alias {
3002 struct alias *next;
3003 char *name;
3004 char *val;
3005 int flag;
3006};
3007
Denis Vlasenko01631112007-12-16 17:20:38 +00003008
3009static struct alias **atab; // [ATABSIZE];
3010#define INIT_G_alias() do { \
3011 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3012} while (0)
3013
Eric Andersen2870d962001-07-02 17:27:21 +00003014
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003015static struct alias **
3016__lookupalias(const char *name) {
3017 unsigned int hashval;
3018 struct alias **app;
3019 const char *p;
3020 unsigned int ch;
3021
3022 p = name;
3023
3024 ch = (unsigned char)*p;
3025 hashval = ch << 4;
3026 while (ch) {
3027 hashval += ch;
3028 ch = (unsigned char)*++p;
3029 }
3030 app = &atab[hashval % ATABSIZE];
3031
3032 for (; *app; app = &(*app)->next) {
3033 if (strcmp(name, (*app)->name) == 0) {
3034 break;
3035 }
3036 }
3037
3038 return app;
3039}
3040
3041static struct alias *
3042lookupalias(const char *name, int check)
3043{
3044 struct alias *ap = *__lookupalias(name);
3045
3046 if (check && ap && (ap->flag & ALIASINUSE))
3047 return NULL;
3048 return ap;
3049}
3050
3051static struct alias *
3052freealias(struct alias *ap)
3053{
3054 struct alias *next;
3055
3056 if (ap->flag & ALIASINUSE) {
3057 ap->flag |= ALIASDEAD;
3058 return ap;
3059 }
3060
3061 next = ap->next;
3062 free(ap->name);
3063 free(ap->val);
3064 free(ap);
3065 return next;
3066}
Eric Andersencb57d552001-06-28 07:25:16 +00003067
Eric Andersenc470f442003-07-28 09:56:35 +00003068static void
3069setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003070{
3071 struct alias *ap, **app;
3072
3073 app = __lookupalias(name);
3074 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003075 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003076 if (ap) {
3077 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003078 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003079 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003080 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003081 ap->flag &= ~ALIASDEAD;
3082 } else {
3083 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003084 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003085 ap->name = ckstrdup(name);
3086 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003087 /*ap->flag = 0; - ckzalloc did it */
3088 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003089 *app = ap;
3090 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003091 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003092}
3093
Eric Andersenc470f442003-07-28 09:56:35 +00003094static int
3095unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003096{
Eric Andersencb57d552001-06-28 07:25:16 +00003097 struct alias **app;
3098
3099 app = __lookupalias(name);
3100
3101 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003102 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003103 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003104 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003105 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003106 }
3107
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003108 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003109}
3110
Eric Andersenc470f442003-07-28 09:56:35 +00003111static void
3112rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003113{
Eric Andersencb57d552001-06-28 07:25:16 +00003114 struct alias *ap, **app;
3115 int i;
3116
Denis Vlasenkob012b102007-02-19 22:43:01 +00003117 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003118 for (i = 0; i < ATABSIZE; i++) {
3119 app = &atab[i];
3120 for (ap = *app; ap; ap = *app) {
3121 *app = freealias(*app);
3122 if (ap == *app) {
3123 app = &ap->next;
3124 }
3125 }
3126 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003127 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003128}
3129
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003130static void
3131printalias(const struct alias *ap)
3132{
3133 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3134}
3135
Eric Andersencb57d552001-06-28 07:25:16 +00003136/*
3137 * TODO - sort output
3138 */
Eric Andersenc470f442003-07-28 09:56:35 +00003139static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003140aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003141{
3142 char *n, *v;
3143 int ret = 0;
3144 struct alias *ap;
3145
Denis Vlasenko68404f12008-03-17 09:00:54 +00003146 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003147 int i;
3148
Denis Vlasenko68404f12008-03-17 09:00:54 +00003149 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003150 for (ap = atab[i]; ap; ap = ap->next) {
3151 printalias(ap);
3152 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003153 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003154 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003155 }
3156 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003157 v = strchr(n+1, '=');
3158 if (v == NULL) { /* n+1: funny ksh stuff */
3159 ap = *__lookupalias(n);
3160 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003161 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003162 ret = 1;
3163 } else
3164 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003165 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003166 *v++ = '\0';
3167 setalias(n, v);
3168 }
3169 }
3170
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003171 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003172}
3173
Eric Andersenc470f442003-07-28 09:56:35 +00003174static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003175unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003176{
3177 int i;
3178
3179 while ((i = nextopt("a")) != '\0') {
3180 if (i == 'a') {
3181 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003182 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003183 }
3184 }
3185 for (i = 0; *argptr; argptr++) {
3186 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003187 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003188 i = 1;
3189 }
3190 }
3191
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003192 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003193}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003194
Denis Vlasenko131ae172007-02-18 13:00:19 +00003195#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003196
Eric Andersenc470f442003-07-28 09:56:35 +00003197
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003198/* ============ jobs.c */
3199
3200/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3201#define FORK_FG 0
3202#define FORK_BG 1
3203#define FORK_NOJOB 2
3204
3205/* mode flags for showjob(s) */
3206#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3207#define SHOW_PID 0x04 /* include process pid */
3208#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3209
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003210/*
3211 * A job structure contains information about a job. A job is either a
3212 * single process or a set of processes contained in a pipeline. In the
3213 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3214 * array of pids.
3215 */
3216
3217struct procstat {
3218 pid_t pid; /* process id */
3219 int status; /* last process status from wait() */
3220 char *cmd; /* text of command being run */
3221};
3222
3223struct job {
3224 struct procstat ps0; /* status of process */
3225 struct procstat *ps; /* status or processes when more than one */
3226#if JOBS
3227 int stopstatus; /* status of a stopped job */
3228#endif
3229 uint32_t
3230 nprocs: 16, /* number of processes */
3231 state: 8,
3232#define JOBRUNNING 0 /* at least one proc running */
3233#define JOBSTOPPED 1 /* all procs are stopped */
3234#define JOBDONE 2 /* all procs are completed */
3235#if JOBS
3236 sigint: 1, /* job was killed by SIGINT */
3237 jobctl: 1, /* job running under job control */
3238#endif
3239 waited: 1, /* true if this entry has been waited for */
3240 used: 1, /* true if this entry is in used */
3241 changed: 1; /* true if status has changed */
3242 struct job *prev_job; /* previous job */
3243};
3244
Denis Vlasenko68404f12008-03-17 09:00:54 +00003245static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003246#if !JOBS
3247#define forkshell(job, node, mode) forkshell(job, mode)
3248#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003249static int forkshell(struct job *, union node *, int);
3250static int waitforjob(struct job *);
3251
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003252#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003253enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003254#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003255#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003256static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003257static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003258#endif
3259
3260/*
3261 * Set the signal handler for the specified signal. The routine figures
3262 * out what it should be set to.
3263 */
3264static void
3265setsignal(int signo)
3266{
3267 int action;
3268 char *t, tsig;
3269 struct sigaction act;
3270
3271 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003272 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003273 if (t == NULL)
3274 action = S_DFL;
3275 else if (*t != '\0')
3276 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003277 if (rootshell && action == S_DFL) {
3278 switch (signo) {
3279 case SIGINT:
3280 if (iflag || minusc || sflag == 0)
3281 action = S_CATCH;
3282 break;
3283 case SIGQUIT:
3284#if DEBUG
3285 if (debug)
3286 break;
3287#endif
3288 /* FALLTHROUGH */
3289 case SIGTERM:
3290 if (iflag)
3291 action = S_IGN;
3292 break;
3293#if JOBS
3294 case SIGTSTP:
3295 case SIGTTOU:
3296 if (mflag)
3297 action = S_IGN;
3298 break;
3299#endif
3300 }
3301 }
3302
3303 t = &sigmode[signo - 1];
3304 tsig = *t;
3305 if (tsig == 0) {
3306 /*
3307 * current setting unknown
3308 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003309 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003310 /*
3311 * Pretend it worked; maybe we should give a warning
3312 * here, but other shells don't. We don't alter
3313 * sigmode, so that we retry every time.
3314 */
3315 return;
3316 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003317 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003318 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003319 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320 if (mflag
3321 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3322 ) {
3323 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003324 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003325 }
3326 }
3327 if (tsig == S_HARD_IGN || tsig == action)
3328 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003329 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003330 switch (action) {
3331 case S_CATCH:
3332 act.sa_handler = onsig;
3333 break;
3334 case S_IGN:
3335 act.sa_handler = SIG_IGN;
3336 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003337 }
3338 *t = action;
3339 act.sa_flags = 0;
3340 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003341 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342}
3343
3344/* mode flags for set_curjob */
3345#define CUR_DELETE 2
3346#define CUR_RUNNING 1
3347#define CUR_STOPPED 0
3348
3349/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003350#define DOWAIT_NONBLOCK WNOHANG
3351#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003352
3353#if JOBS
3354/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003355static int initialpgrp; //references:2
3356static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003357#endif
3358/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003359static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003360/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003361static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003363static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003365static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003366
3367static void
3368set_curjob(struct job *jp, unsigned mode)
3369{
3370 struct job *jp1;
3371 struct job **jpp, **curp;
3372
3373 /* first remove from list */
3374 jpp = curp = &curjob;
3375 do {
3376 jp1 = *jpp;
3377 if (jp1 == jp)
3378 break;
3379 jpp = &jp1->prev_job;
3380 } while (1);
3381 *jpp = jp1->prev_job;
3382
3383 /* Then re-insert in correct position */
3384 jpp = curp;
3385 switch (mode) {
3386 default:
3387#if DEBUG
3388 abort();
3389#endif
3390 case CUR_DELETE:
3391 /* job being deleted */
3392 break;
3393 case CUR_RUNNING:
3394 /* newly created job or backgrounded job,
3395 put after all stopped jobs. */
3396 do {
3397 jp1 = *jpp;
3398#if JOBS
3399 if (!jp1 || jp1->state != JOBSTOPPED)
3400#endif
3401 break;
3402 jpp = &jp1->prev_job;
3403 } while (1);
3404 /* FALLTHROUGH */
3405#if JOBS
3406 case CUR_STOPPED:
3407#endif
3408 /* newly stopped job - becomes curjob */
3409 jp->prev_job = *jpp;
3410 *jpp = jp;
3411 break;
3412 }
3413}
3414
3415#if JOBS || DEBUG
3416static int
3417jobno(const struct job *jp)
3418{
3419 return jp - jobtab + 1;
3420}
3421#endif
3422
3423/*
3424 * Convert a job name to a job structure.
3425 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003426#if !JOBS
3427#define getjob(name, getctl) getjob(name)
3428#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003429static struct job *
3430getjob(const char *name, int getctl)
3431{
3432 struct job *jp;
3433 struct job *found;
3434 const char *err_msg = "No such job: %s";
3435 unsigned num;
3436 int c;
3437 const char *p;
3438 char *(*match)(const char *, const char *);
3439
3440 jp = curjob;
3441 p = name;
3442 if (!p)
3443 goto currentjob;
3444
3445 if (*p != '%')
3446 goto err;
3447
3448 c = *++p;
3449 if (!c)
3450 goto currentjob;
3451
3452 if (!p[1]) {
3453 if (c == '+' || c == '%') {
3454 currentjob:
3455 err_msg = "No current job";
3456 goto check;
3457 }
3458 if (c == '-') {
3459 if (jp)
3460 jp = jp->prev_job;
3461 err_msg = "No previous job";
3462 check:
3463 if (!jp)
3464 goto err;
3465 goto gotit;
3466 }
3467 }
3468
3469 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003470// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003471 num = atoi(p);
3472 if (num < njobs) {
3473 jp = jobtab + num - 1;
3474 if (jp->used)
3475 goto gotit;
3476 goto err;
3477 }
3478 }
3479
3480 match = prefix;
3481 if (*p == '?') {
3482 match = strstr;
3483 p++;
3484 }
3485
3486 found = 0;
3487 while (1) {
3488 if (!jp)
3489 goto err;
3490 if (match(jp->ps[0].cmd, p)) {
3491 if (found)
3492 goto err;
3493 found = jp;
3494 err_msg = "%s: ambiguous";
3495 }
3496 jp = jp->prev_job;
3497 }
3498
3499 gotit:
3500#if JOBS
3501 err_msg = "job %s not created under job control";
3502 if (getctl && jp->jobctl == 0)
3503 goto err;
3504#endif
3505 return jp;
3506 err:
3507 ash_msg_and_raise_error(err_msg, name);
3508}
3509
3510/*
3511 * Mark a job structure as unused.
3512 */
3513static void
3514freejob(struct job *jp)
3515{
3516 struct procstat *ps;
3517 int i;
3518
3519 INT_OFF;
3520 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3521 if (ps->cmd != nullstr)
3522 free(ps->cmd);
3523 }
3524 if (jp->ps != &jp->ps0)
3525 free(jp->ps);
3526 jp->used = 0;
3527 set_curjob(jp, CUR_DELETE);
3528 INT_ON;
3529}
3530
3531#if JOBS
3532static void
3533xtcsetpgrp(int fd, pid_t pgrp)
3534{
3535 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003536 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003537}
3538
3539/*
3540 * Turn job control on and off.
3541 *
3542 * Note: This code assumes that the third arg to ioctl is a character
3543 * pointer, which is true on Berkeley systems but not System V. Since
3544 * System V doesn't have job control yet, this isn't a problem now.
3545 *
3546 * Called with interrupts off.
3547 */
3548static void
3549setjobctl(int on)
3550{
3551 int fd;
3552 int pgrp;
3553
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003554 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003555 return;
3556 if (on) {
3557 int ofd;
3558 ofd = fd = open(_PATH_TTY, O_RDWR);
3559 if (fd < 0) {
3560 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3561 * That sometimes helps to acquire controlling tty.
3562 * Obviously, a workaround for bugs when someone
3563 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003564 fd = 2;
3565 while (!isatty(fd))
3566 if (--fd < 0)
3567 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003568 }
3569 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003570 if (ofd >= 0)
3571 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003572 if (fd < 0)
3573 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003574 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003575 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003576 do { /* while we are in the background */
3577 pgrp = tcgetpgrp(fd);
3578 if (pgrp < 0) {
3579 out:
3580 ash_msg("can't access tty; job control turned off");
3581 mflag = on = 0;
3582 goto close;
3583 }
3584 if (pgrp == getpgrp())
3585 break;
3586 killpg(0, SIGTTIN);
3587 } while (1);
3588 initialpgrp = pgrp;
3589
3590 setsignal(SIGTSTP);
3591 setsignal(SIGTTOU);
3592 setsignal(SIGTTIN);
3593 pgrp = rootpid;
3594 setpgid(0, pgrp);
3595 xtcsetpgrp(fd, pgrp);
3596 } else {
3597 /* turning job control off */
3598 fd = ttyfd;
3599 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003600 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003601 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003602 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003603 setpgid(0, pgrp);
3604 setsignal(SIGTSTP);
3605 setsignal(SIGTTOU);
3606 setsignal(SIGTTIN);
3607 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003608 if (fd >= 0)
3609 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003610 fd = -1;
3611 }
3612 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003613 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003614}
3615
3616static int
3617killcmd(int argc, char **argv)
3618{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003619 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003620 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003621 do {
3622 if (argv[i][0] == '%') {
3623 struct job *jp = getjob(argv[i], 0);
3624 unsigned pid = jp->ps[0].pid;
3625 /* Enough space for ' -NNN<nul>' */
3626 argv[i] = alloca(sizeof(int)*3 + 3);
3627 /* kill_main has matching code to expect
3628 * leading space. Needed to not confuse
3629 * negative pids with "kill -SIGNAL_NO" syntax */
3630 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003631 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003632 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003633 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003634 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003635}
3636
3637static void
3638showpipe(struct job *jp, FILE *out)
3639{
3640 struct procstat *sp;
3641 struct procstat *spend;
3642
3643 spend = jp->ps + jp->nprocs;
3644 for (sp = jp->ps + 1; sp < spend; sp++)
3645 fprintf(out, " | %s", sp->cmd);
3646 outcslow('\n', out);
3647 flush_stdout_stderr();
3648}
3649
3650
3651static int
3652restartjob(struct job *jp, int mode)
3653{
3654 struct procstat *ps;
3655 int i;
3656 int status;
3657 pid_t pgid;
3658
3659 INT_OFF;
3660 if (jp->state == JOBDONE)
3661 goto out;
3662 jp->state = JOBRUNNING;
3663 pgid = jp->ps->pid;
3664 if (mode == FORK_FG)
3665 xtcsetpgrp(ttyfd, pgid);
3666 killpg(pgid, SIGCONT);
3667 ps = jp->ps;
3668 i = jp->nprocs;
3669 do {
3670 if (WIFSTOPPED(ps->status)) {
3671 ps->status = -1;
3672 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003673 ps++;
3674 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003675 out:
3676 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3677 INT_ON;
3678 return status;
3679}
3680
3681static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003682fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003683{
3684 struct job *jp;
3685 FILE *out;
3686 int mode;
3687 int retval;
3688
3689 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3690 nextopt(nullstr);
3691 argv = argptr;
3692 out = stdout;
3693 do {
3694 jp = getjob(*argv, 1);
3695 if (mode == FORK_BG) {
3696 set_curjob(jp, CUR_RUNNING);
3697 fprintf(out, "[%d] ", jobno(jp));
3698 }
3699 outstr(jp->ps->cmd, out);
3700 showpipe(jp, out);
3701 retval = restartjob(jp, mode);
3702 } while (*argv && *++argv);
3703 return retval;
3704}
3705#endif
3706
3707static int
3708sprint_status(char *s, int status, int sigonly)
3709{
3710 int col;
3711 int st;
3712
3713 col = 0;
3714 if (!WIFEXITED(status)) {
3715#if JOBS
3716 if (WIFSTOPPED(status))
3717 st = WSTOPSIG(status);
3718 else
3719#endif
3720 st = WTERMSIG(status);
3721 if (sigonly) {
3722 if (st == SIGINT || st == SIGPIPE)
3723 goto out;
3724#if JOBS
3725 if (WIFSTOPPED(status))
3726 goto out;
3727#endif
3728 }
3729 st &= 0x7f;
3730 col = fmtstr(s, 32, strsignal(st));
3731 if (WCOREDUMP(status)) {
3732 col += fmtstr(s + col, 16, " (core dumped)");
3733 }
3734 } else if (!sigonly) {
3735 st = WEXITSTATUS(status);
3736 if (st)
3737 col = fmtstr(s, 16, "Done(%d)", st);
3738 else
3739 col = fmtstr(s, 16, "Done");
3740 }
3741 out:
3742 return col;
3743}
3744
3745/*
3746 * Do a wait system call. If job control is compiled in, we accept
3747 * stopped processes. If block is zero, we return a value of zero
3748 * rather than blocking.
3749 *
3750 * System V doesn't have a non-blocking wait system call. It does
3751 * have a SIGCLD signal that is sent to a process when one of it's
3752 * children dies. The obvious way to use SIGCLD would be to install
3753 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3754 * was received, and have waitproc bump another counter when it got
3755 * the status of a process. Waitproc would then know that a wait
3756 * system call would not block if the two counters were different.
3757 * This approach doesn't work because if a process has children that
3758 * have not been waited for, System V will send it a SIGCLD when it
3759 * installs a signal handler for SIGCLD. What this means is that when
3760 * a child exits, the shell will be sent SIGCLD signals continuously
3761 * until is runs out of stack space, unless it does a wait call before
3762 * restoring the signal handler. The code below takes advantage of
3763 * this (mis)feature by installing a signal handler for SIGCLD and
3764 * then checking to see whether it was called. If there are any
3765 * children to be waited for, it will be.
3766 *
3767 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3768 * waits at all. In this case, the user will not be informed when
3769 * a background process until the next time she runs a real program
3770 * (as opposed to running a builtin command or just typing return),
3771 * and the jobs command may give out of date information.
3772 */
3773static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003774waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003775{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003776#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003777 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003778 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003780 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3781 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003782}
3783
3784/*
3785 * Wait for a process to terminate.
3786 */
3787static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003788dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003789{
3790 int pid;
3791 int status;
3792 struct job *jp;
3793 struct job *thisjob;
3794 int state;
3795
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003796 TRACE(("dowait(%d) called\n", wait_flags));
3797 pid = waitproc(wait_flags, &status);
3798 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003799 if (pid <= 0) {
3800 /* If we were doing blocking wait and (probably) got EINTR,
3801 * check for pending sigs received while waiting.
3802 * (NB: can be moved into callers if needed) */
3803 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3804 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003805 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003806 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003807 INT_OFF;
3808 thisjob = NULL;
3809 for (jp = curjob; jp; jp = jp->prev_job) {
3810 struct procstat *sp;
3811 struct procstat *spend;
3812 if (jp->state == JOBDONE)
3813 continue;
3814 state = JOBDONE;
3815 spend = jp->ps + jp->nprocs;
3816 sp = jp->ps;
3817 do {
3818 if (sp->pid == pid) {
3819 TRACE(("Job %d: changing status of proc %d "
3820 "from 0x%x to 0x%x\n",
3821 jobno(jp), pid, sp->status, status));
3822 sp->status = status;
3823 thisjob = jp;
3824 }
3825 if (sp->status == -1)
3826 state = JOBRUNNING;
3827#if JOBS
3828 if (state == JOBRUNNING)
3829 continue;
3830 if (WIFSTOPPED(sp->status)) {
3831 jp->stopstatus = sp->status;
3832 state = JOBSTOPPED;
3833 }
3834#endif
3835 } while (++sp < spend);
3836 if (thisjob)
3837 goto gotjob;
3838 }
3839#if JOBS
3840 if (!WIFSTOPPED(status))
3841#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842 jobless--;
3843 goto out;
3844
3845 gotjob:
3846 if (state != JOBRUNNING) {
3847 thisjob->changed = 1;
3848
3849 if (thisjob->state != state) {
3850 TRACE(("Job %d: changing state from %d to %d\n",
3851 jobno(thisjob), thisjob->state, state));
3852 thisjob->state = state;
3853#if JOBS
3854 if (state == JOBSTOPPED) {
3855 set_curjob(thisjob, CUR_STOPPED);
3856 }
3857#endif
3858 }
3859 }
3860
3861 out:
3862 INT_ON;
3863
3864 if (thisjob && thisjob == job) {
3865 char s[48 + 1];
3866 int len;
3867
3868 len = sprint_status(s, status, 1);
3869 if (len) {
3870 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003871 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003872 out2str(s);
3873 }
3874 }
3875 return pid;
3876}
3877
3878#if JOBS
3879static void
3880showjob(FILE *out, struct job *jp, int mode)
3881{
3882 struct procstat *ps;
3883 struct procstat *psend;
3884 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003885 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003886 char s[80];
3887
3888 ps = jp->ps;
3889
3890 if (mode & SHOW_PGID) {
3891 /* just output process (group) id of pipeline */
3892 fprintf(out, "%d\n", ps->pid);
3893 return;
3894 }
3895
3896 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003897 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003898
3899 if (jp == curjob)
3900 s[col - 2] = '+';
3901 else if (curjob && jp == curjob->prev_job)
3902 s[col - 2] = '-';
3903
3904 if (mode & SHOW_PID)
3905 col += fmtstr(s + col, 16, "%d ", ps->pid);
3906
3907 psend = ps + jp->nprocs;
3908
3909 if (jp->state == JOBRUNNING) {
3910 strcpy(s + col, "Running");
3911 col += sizeof("Running") - 1;
3912 } else {
3913 int status = psend[-1].status;
3914 if (jp->state == JOBSTOPPED)
3915 status = jp->stopstatus;
3916 col += sprint_status(s + col, status, 0);
3917 }
3918
3919 goto start;
3920
3921 do {
3922 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003923 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003924 start:
3925 fprintf(out, "%s%*c%s",
3926 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3927 );
3928 if (!(mode & SHOW_PID)) {
3929 showpipe(jp, out);
3930 break;
3931 }
3932 if (++ps == psend) {
3933 outcslow('\n', out);
3934 break;
3935 }
3936 } while (1);
3937
3938 jp->changed = 0;
3939
3940 if (jp->state == JOBDONE) {
3941 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3942 freejob(jp);
3943 }
3944}
3945
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003946/*
3947 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3948 * statuses have changed since the last call to showjobs.
3949 */
3950static void
3951showjobs(FILE *out, int mode)
3952{
3953 struct job *jp;
3954
3955 TRACE(("showjobs(%x) called\n", mode));
3956
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003957 /* If not even one job changed, there is nothing to do */
3958 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003959 continue;
3960
3961 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003962 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003963 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003964 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003965 }
3966}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003967
3968static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003969jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003970{
3971 int mode, m;
3972
3973 mode = 0;
3974 while ((m = nextopt("lp"))) {
3975 if (m == 'l')
3976 mode = SHOW_PID;
3977 else
3978 mode = SHOW_PGID;
3979 }
3980
3981 argv = argptr;
3982 if (*argv) {
3983 do
3984 showjob(stdout, getjob(*argv,0), mode);
3985 while (*++argv);
3986 } else
3987 showjobs(stdout, mode);
3988
3989 return 0;
3990}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003991#endif /* JOBS */
3992
3993static int
3994getstatus(struct job *job)
3995{
3996 int status;
3997 int retval;
3998
3999 status = job->ps[job->nprocs - 1].status;
4000 retval = WEXITSTATUS(status);
4001 if (!WIFEXITED(status)) {
4002#if JOBS
4003 retval = WSTOPSIG(status);
4004 if (!WIFSTOPPED(status))
4005#endif
4006 {
4007 /* XXX: limits number of signals */
4008 retval = WTERMSIG(status);
4009#if JOBS
4010 if (retval == SIGINT)
4011 job->sigint = 1;
4012#endif
4013 }
4014 retval += 128;
4015 }
4016 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4017 jobno(job), job->nprocs, status, retval));
4018 return retval;
4019}
4020
4021static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004022waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004023{
4024 struct job *job;
4025 int retval;
4026 struct job *jp;
4027
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004028// exsig++;
4029// xbarrier();
4030 if (pendingsig)
4031 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004032
4033 nextopt(nullstr);
4034 retval = 0;
4035
4036 argv = argptr;
4037 if (!*argv) {
4038 /* wait for all jobs */
4039 for (;;) {
4040 jp = curjob;
4041 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004042 if (!jp) /* no running procs */
4043 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004044 if (jp->state == JOBRUNNING)
4045 break;
4046 jp->waited = 1;
4047 jp = jp->prev_job;
4048 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004049 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050 }
4051 }
4052
4053 retval = 127;
4054 do {
4055 if (**argv != '%') {
4056 pid_t pid = number(*argv);
4057 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004058 while (1) {
4059 if (!job)
4060 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004061 if (job->ps[job->nprocs - 1].pid == pid)
4062 break;
4063 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004064 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004065 } else
4066 job = getjob(*argv, 0);
4067 /* loop until process terminated or stopped */
4068 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004069 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004070 job->waited = 1;
4071 retval = getstatus(job);
4072 repeat:
4073 ;
4074 } while (*++argv);
4075
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004076 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004077 return retval;
4078}
4079
4080static struct job *
4081growjobtab(void)
4082{
4083 size_t len;
4084 ptrdiff_t offset;
4085 struct job *jp, *jq;
4086
4087 len = njobs * sizeof(*jp);
4088 jq = jobtab;
4089 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4090
4091 offset = (char *)jp - (char *)jq;
4092 if (offset) {
4093 /* Relocate pointers */
4094 size_t l = len;
4095
4096 jq = (struct job *)((char *)jq + l);
4097 while (l) {
4098 l -= sizeof(*jp);
4099 jq--;
4100#define joff(p) ((struct job *)((char *)(p) + l))
4101#define jmove(p) (p) = (void *)((char *)(p) + offset)
4102 if (joff(jp)->ps == &jq->ps0)
4103 jmove(joff(jp)->ps);
4104 if (joff(jp)->prev_job)
4105 jmove(joff(jp)->prev_job);
4106 }
4107 if (curjob)
4108 jmove(curjob);
4109#undef joff
4110#undef jmove
4111 }
4112
4113 njobs += 4;
4114 jobtab = jp;
4115 jp = (struct job *)((char *)jp + len);
4116 jq = jp + 3;
4117 do {
4118 jq->used = 0;
4119 } while (--jq >= jp);
4120 return jp;
4121}
4122
4123/*
4124 * Return a new job structure.
4125 * Called with interrupts off.
4126 */
4127static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004128makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004129{
4130 int i;
4131 struct job *jp;
4132
4133 for (i = njobs, jp = jobtab; ; jp++) {
4134 if (--i < 0) {
4135 jp = growjobtab();
4136 break;
4137 }
4138 if (jp->used == 0)
4139 break;
4140 if (jp->state != JOBDONE || !jp->waited)
4141 continue;
4142#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004143 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004144 continue;
4145#endif
4146 freejob(jp);
4147 break;
4148 }
4149 memset(jp, 0, sizeof(*jp));
4150#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004151 /* jp->jobctl is a bitfield.
4152 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004153 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004154 jp->jobctl = 1;
4155#endif
4156 jp->prev_job = curjob;
4157 curjob = jp;
4158 jp->used = 1;
4159 jp->ps = &jp->ps0;
4160 if (nprocs > 1) {
4161 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4162 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004163 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004164 jobno(jp)));
4165 return jp;
4166}
4167
4168#if JOBS
4169/*
4170 * Return a string identifying a command (to be printed by the
4171 * jobs command).
4172 */
4173static char *cmdnextc;
4174
4175static void
4176cmdputs(const char *s)
4177{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004178 static const char vstype[VSTYPE + 1][3] = {
4179 "", "}", "-", "+", "?", "=",
4180 "%", "%%", "#", "##"
4181 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4182 };
4183
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184 const char *p, *str;
4185 char c, cc[2] = " ";
4186 char *nextc;
4187 int subtype = 0;
4188 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004189
4190 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4191 p = s;
4192 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004193 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004194 switch (c) {
4195 case CTLESC:
4196 c = *p++;
4197 break;
4198 case CTLVAR:
4199 subtype = *p++;
4200 if ((subtype & VSTYPE) == VSLENGTH)
4201 str = "${#";
4202 else
4203 str = "${";
4204 if (!(subtype & VSQUOTE) == !(quoted & 1))
4205 goto dostr;
4206 quoted ^= 1;
4207 c = '"';
4208 break;
4209 case CTLENDVAR:
4210 str = "\"}" + !(quoted & 1);
4211 quoted >>= 1;
4212 subtype = 0;
4213 goto dostr;
4214 case CTLBACKQ:
4215 str = "$(...)";
4216 goto dostr;
4217 case CTLBACKQ+CTLQUOTE:
4218 str = "\"$(...)\"";
4219 goto dostr;
4220#if ENABLE_ASH_MATH_SUPPORT
4221 case CTLARI:
4222 str = "$((";
4223 goto dostr;
4224 case CTLENDARI:
4225 str = "))";
4226 goto dostr;
4227#endif
4228 case CTLQUOTEMARK:
4229 quoted ^= 1;
4230 c = '"';
4231 break;
4232 case '=':
4233 if (subtype == 0)
4234 break;
4235 if ((subtype & VSTYPE) != VSNORMAL)
4236 quoted <<= 1;
4237 str = vstype[subtype & VSTYPE];
4238 if (subtype & VSNUL)
4239 c = ':';
4240 else
4241 goto checkstr;
4242 break;
4243 case '\'':
4244 case '\\':
4245 case '"':
4246 case '$':
4247 /* These can only happen inside quotes */
4248 cc[0] = c;
4249 str = cc;
4250 c = '\\';
4251 break;
4252 default:
4253 break;
4254 }
4255 USTPUTC(c, nextc);
4256 checkstr:
4257 if (!str)
4258 continue;
4259 dostr:
4260 while ((c = *str++)) {
4261 USTPUTC(c, nextc);
4262 }
4263 }
4264 if (quoted & 1) {
4265 USTPUTC('"', nextc);
4266 }
4267 *nextc = 0;
4268 cmdnextc = nextc;
4269}
4270
4271/* cmdtxt() and cmdlist() call each other */
4272static void cmdtxt(union node *n);
4273
4274static void
4275cmdlist(union node *np, int sep)
4276{
4277 for (; np; np = np->narg.next) {
4278 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004279 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004280 cmdtxt(np);
4281 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004282 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004283 }
4284}
4285
4286static void
4287cmdtxt(union node *n)
4288{
4289 union node *np;
4290 struct nodelist *lp;
4291 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004292
4293 if (!n)
4294 return;
4295 switch (n->type) {
4296 default:
4297#if DEBUG
4298 abort();
4299#endif
4300 case NPIPE:
4301 lp = n->npipe.cmdlist;
4302 for (;;) {
4303 cmdtxt(lp->n);
4304 lp = lp->next;
4305 if (!lp)
4306 break;
4307 cmdputs(" | ");
4308 }
4309 break;
4310 case NSEMI:
4311 p = "; ";
4312 goto binop;
4313 case NAND:
4314 p = " && ";
4315 goto binop;
4316 case NOR:
4317 p = " || ";
4318 binop:
4319 cmdtxt(n->nbinary.ch1);
4320 cmdputs(p);
4321 n = n->nbinary.ch2;
4322 goto donode;
4323 case NREDIR:
4324 case NBACKGND:
4325 n = n->nredir.n;
4326 goto donode;
4327 case NNOT:
4328 cmdputs("!");
4329 n = n->nnot.com;
4330 donode:
4331 cmdtxt(n);
4332 break;
4333 case NIF:
4334 cmdputs("if ");
4335 cmdtxt(n->nif.test);
4336 cmdputs("; then ");
4337 n = n->nif.ifpart;
4338 if (n->nif.elsepart) {
4339 cmdtxt(n);
4340 cmdputs("; else ");
4341 n = n->nif.elsepart;
4342 }
4343 p = "; fi";
4344 goto dotail;
4345 case NSUBSHELL:
4346 cmdputs("(");
4347 n = n->nredir.n;
4348 p = ")";
4349 goto dotail;
4350 case NWHILE:
4351 p = "while ";
4352 goto until;
4353 case NUNTIL:
4354 p = "until ";
4355 until:
4356 cmdputs(p);
4357 cmdtxt(n->nbinary.ch1);
4358 n = n->nbinary.ch2;
4359 p = "; done";
4360 dodo:
4361 cmdputs("; do ");
4362 dotail:
4363 cmdtxt(n);
4364 goto dotail2;
4365 case NFOR:
4366 cmdputs("for ");
4367 cmdputs(n->nfor.var);
4368 cmdputs(" in ");
4369 cmdlist(n->nfor.args, 1);
4370 n = n->nfor.body;
4371 p = "; done";
4372 goto dodo;
4373 case NDEFUN:
4374 cmdputs(n->narg.text);
4375 p = "() { ... }";
4376 goto dotail2;
4377 case NCMD:
4378 cmdlist(n->ncmd.args, 1);
4379 cmdlist(n->ncmd.redirect, 0);
4380 break;
4381 case NARG:
4382 p = n->narg.text;
4383 dotail2:
4384 cmdputs(p);
4385 break;
4386 case NHERE:
4387 case NXHERE:
4388 p = "<<...";
4389 goto dotail2;
4390 case NCASE:
4391 cmdputs("case ");
4392 cmdputs(n->ncase.expr->narg.text);
4393 cmdputs(" in ");
4394 for (np = n->ncase.cases; np; np = np->nclist.next) {
4395 cmdtxt(np->nclist.pattern);
4396 cmdputs(") ");
4397 cmdtxt(np->nclist.body);
4398 cmdputs(";; ");
4399 }
4400 p = "esac";
4401 goto dotail2;
4402 case NTO:
4403 p = ">";
4404 goto redir;
4405 case NCLOBBER:
4406 p = ">|";
4407 goto redir;
4408 case NAPPEND:
4409 p = ">>";
4410 goto redir;
4411 case NTOFD:
4412 p = ">&";
4413 goto redir;
4414 case NFROM:
4415 p = "<";
4416 goto redir;
4417 case NFROMFD:
4418 p = "<&";
4419 goto redir;
4420 case NFROMTO:
4421 p = "<>";
4422 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004423 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004424 cmdputs(p);
4425 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004426 cmdputs(utoa(n->ndup.dupfd));
4427 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004428 }
4429 n = n->nfile.fname;
4430 goto donode;
4431 }
4432}
4433
4434static char *
4435commandtext(union node *n)
4436{
4437 char *name;
4438
4439 STARTSTACKSTR(cmdnextc);
4440 cmdtxt(n);
4441 name = stackblock();
4442 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4443 name, cmdnextc, cmdnextc));
4444 return ckstrdup(name);
4445}
4446#endif /* JOBS */
4447
4448/*
4449 * Fork off a subshell. If we are doing job control, give the subshell its
4450 * own process group. Jp is a job structure that the job is to be added to.
4451 * N is the command that will be evaluated by the child. Both jp and n may
4452 * be NULL. The mode parameter can be one of the following:
4453 * FORK_FG - Fork off a foreground process.
4454 * FORK_BG - Fork off a background process.
4455 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4456 * process group even if job control is on.
4457 *
4458 * When job control is turned off, background processes have their standard
4459 * input redirected to /dev/null (except for the second and later processes
4460 * in a pipeline).
4461 *
4462 * Called with interrupts off.
4463 */
4464/*
4465 * Clear traps on a fork.
4466 */
4467static void
4468clear_traps(void)
4469{
4470 char **tp;
4471
4472 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004473 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004474 INT_OFF;
4475 free(*tp);
4476 *tp = NULL;
4477 if (tp != &trap[0])
4478 setsignal(tp - trap);
4479 INT_ON;
4480 }
4481 }
4482}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004483
4484/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004486
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004487/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004489forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004490{
4491 int oldlvl;
4492
4493 TRACE(("Child shell %d\n", getpid()));
4494 oldlvl = shlvl;
4495 shlvl++;
4496
4497 closescript();
4498 clear_traps();
4499#if JOBS
4500 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004501 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004502 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4503 pid_t pgrp;
4504
4505 if (jp->nprocs == 0)
4506 pgrp = getpid();
4507 else
4508 pgrp = jp->ps[0].pid;
4509 /* This can fail because we are doing it in the parent also */
4510 (void)setpgid(0, pgrp);
4511 if (mode == FORK_FG)
4512 xtcsetpgrp(ttyfd, pgrp);
4513 setsignal(SIGTSTP);
4514 setsignal(SIGTTOU);
4515 } else
4516#endif
4517 if (mode == FORK_BG) {
4518 ignoresig(SIGINT);
4519 ignoresig(SIGQUIT);
4520 if (jp->nprocs == 0) {
4521 close(0);
4522 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004523 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004524 }
4525 }
4526 if (!oldlvl && iflag) {
4527 setsignal(SIGINT);
4528 setsignal(SIGQUIT);
4529 setsignal(SIGTERM);
4530 }
4531 for (jp = curjob; jp; jp = jp->prev_job)
4532 freejob(jp);
4533 jobless = 0;
4534}
4535
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004536/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004537#if !JOBS
4538#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4539#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004540static void
4541forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4542{
4543 TRACE(("In parent shell: child = %d\n", pid));
4544 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004545 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4546 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004547 jobless++;
4548 return;
4549 }
4550#if JOBS
4551 if (mode != FORK_NOJOB && jp->jobctl) {
4552 int pgrp;
4553
4554 if (jp->nprocs == 0)
4555 pgrp = pid;
4556 else
4557 pgrp = jp->ps[0].pid;
4558 /* This can fail because we are doing it in the child also */
4559 setpgid(pid, pgrp);
4560 }
4561#endif
4562 if (mode == FORK_BG) {
4563 backgndpid = pid; /* set $! */
4564 set_curjob(jp, CUR_RUNNING);
4565 }
4566 if (jp) {
4567 struct procstat *ps = &jp->ps[jp->nprocs++];
4568 ps->pid = pid;
4569 ps->status = -1;
4570 ps->cmd = nullstr;
4571#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004572 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004573 ps->cmd = commandtext(n);
4574#endif
4575 }
4576}
4577
4578static int
4579forkshell(struct job *jp, union node *n, int mode)
4580{
4581 int pid;
4582
4583 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4584 pid = fork();
4585 if (pid < 0) {
4586 TRACE(("Fork failed, errno=%d", errno));
4587 if (jp)
4588 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004589 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004590 }
4591 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004592 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004593 else
4594 forkparent(jp, n, mode, pid);
4595 return pid;
4596}
4597
4598/*
4599 * Wait for job to finish.
4600 *
4601 * Under job control we have the problem that while a child process is
4602 * running interrupts generated by the user are sent to the child but not
4603 * to the shell. This means that an infinite loop started by an inter-
4604 * active user may be hard to kill. With job control turned off, an
4605 * interactive user may place an interactive program inside a loop. If
4606 * the interactive program catches interrupts, the user doesn't want
4607 * these interrupts to also abort the loop. The approach we take here
4608 * is to have the shell ignore interrupt signals while waiting for a
4609 * foreground process to terminate, and then send itself an interrupt
4610 * signal if the child process was terminated by an interrupt signal.
4611 * Unfortunately, some programs want to do a bit of cleanup and then
4612 * exit on interrupt; unless these processes terminate themselves by
4613 * sending a signal to themselves (instead of calling exit) they will
4614 * confuse this approach.
4615 *
4616 * Called with interrupts off.
4617 */
4618static int
4619waitforjob(struct job *jp)
4620{
4621 int st;
4622
4623 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4624 while (jp->state == JOBRUNNING) {
4625 dowait(DOWAIT_BLOCK, jp);
4626 }
4627 st = getstatus(jp);
4628#if JOBS
4629 if (jp->jobctl) {
4630 xtcsetpgrp(ttyfd, rootpid);
4631 /*
4632 * This is truly gross.
4633 * If we're doing job control, then we did a TIOCSPGRP which
4634 * caused us (the shell) to no longer be in the controlling
4635 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4636 * intuit from the subprocess exit status whether a SIGINT
4637 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4638 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004639 if (jp->sigint) /* TODO: do the same with all signals */
4640 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004641 }
4642 if (jp->state == JOBDONE)
4643#endif
4644 freejob(jp);
4645 return st;
4646}
4647
4648/*
4649 * return 1 if there are stopped jobs, otherwise 0
4650 */
4651static int
4652stoppedjobs(void)
4653{
4654 struct job *jp;
4655 int retval;
4656
4657 retval = 0;
4658 if (job_warning)
4659 goto out;
4660 jp = curjob;
4661 if (jp && jp->state == JOBSTOPPED) {
4662 out2str("You have stopped jobs.\n");
4663 job_warning = 2;
4664 retval++;
4665 }
4666 out:
4667 return retval;
4668}
4669
4670
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004671/* ============ redir.c
4672 *
4673 * Code for dealing with input/output redirection.
4674 */
4675
4676#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004677#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004678
4679/*
4680 * Open a file in noclobber mode.
4681 * The code was copied from bash.
4682 */
4683static int
4684noclobberopen(const char *fname)
4685{
4686 int r, fd;
4687 struct stat finfo, finfo2;
4688
4689 /*
4690 * If the file exists and is a regular file, return an error
4691 * immediately.
4692 */
4693 r = stat(fname, &finfo);
4694 if (r == 0 && S_ISREG(finfo.st_mode)) {
4695 errno = EEXIST;
4696 return -1;
4697 }
4698
4699 /*
4700 * If the file was not present (r != 0), make sure we open it
4701 * exclusively so that if it is created before we open it, our open
4702 * will fail. Make sure that we do not truncate an existing file.
4703 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4704 * file was not a regular file, we leave O_EXCL off.
4705 */
4706 if (r != 0)
4707 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4708 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4709
4710 /* If the open failed, return the file descriptor right away. */
4711 if (fd < 0)
4712 return fd;
4713
4714 /*
4715 * OK, the open succeeded, but the file may have been changed from a
4716 * non-regular file to a regular file between the stat and the open.
4717 * We are assuming that the O_EXCL open handles the case where FILENAME
4718 * did not exist and is symlinked to an existing file between the stat
4719 * and open.
4720 */
4721
4722 /*
4723 * If we can open it and fstat the file descriptor, and neither check
4724 * revealed that it was a regular file, and the file has not been
4725 * replaced, return the file descriptor.
4726 */
4727 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4728 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4729 return fd;
4730
4731 /* The file has been replaced. badness. */
4732 close(fd);
4733 errno = EEXIST;
4734 return -1;
4735}
4736
4737/*
4738 * Handle here documents. Normally we fork off a process to write the
4739 * data to a pipe. If the document is short, we can stuff the data in
4740 * the pipe without forking.
4741 */
4742/* openhere needs this forward reference */
4743static void expandhere(union node *arg, int fd);
4744static int
4745openhere(union node *redir)
4746{
4747 int pip[2];
4748 size_t len = 0;
4749
4750 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004751 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004752 if (redir->type == NHERE) {
4753 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004754 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004755 full_write(pip[1], redir->nhere.doc->narg.text, len);
4756 goto out;
4757 }
4758 }
4759 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004760 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004761 close(pip[0]);
4762 signal(SIGINT, SIG_IGN);
4763 signal(SIGQUIT, SIG_IGN);
4764 signal(SIGHUP, SIG_IGN);
4765#ifdef SIGTSTP
4766 signal(SIGTSTP, SIG_IGN);
4767#endif
4768 signal(SIGPIPE, SIG_DFL);
4769 if (redir->type == NHERE)
4770 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004771 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004772 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004773 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004774 }
4775 out:
4776 close(pip[1]);
4777 return pip[0];
4778}
4779
4780static int
4781openredirect(union node *redir)
4782{
4783 char *fname;
4784 int f;
4785
4786 switch (redir->nfile.type) {
4787 case NFROM:
4788 fname = redir->nfile.expfname;
4789 f = open(fname, O_RDONLY);
4790 if (f < 0)
4791 goto eopen;
4792 break;
4793 case NFROMTO:
4794 fname = redir->nfile.expfname;
4795 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4796 if (f < 0)
4797 goto ecreate;
4798 break;
4799 case NTO:
4800 /* Take care of noclobber mode. */
4801 if (Cflag) {
4802 fname = redir->nfile.expfname;
4803 f = noclobberopen(fname);
4804 if (f < 0)
4805 goto ecreate;
4806 break;
4807 }
4808 /* FALLTHROUGH */
4809 case NCLOBBER:
4810 fname = redir->nfile.expfname;
4811 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4812 if (f < 0)
4813 goto ecreate;
4814 break;
4815 case NAPPEND:
4816 fname = redir->nfile.expfname;
4817 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4818 if (f < 0)
4819 goto ecreate;
4820 break;
4821 default:
4822#if DEBUG
4823 abort();
4824#endif
4825 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004826/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004827// case NTOFD:
4828// case NFROMFD:
4829// f = -1;
4830// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004831 case NHERE:
4832 case NXHERE:
4833 f = openhere(redir);
4834 break;
4835 }
4836
4837 return f;
4838 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004839 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004840 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004841 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004842}
4843
4844/*
4845 * Copy a file descriptor to be >= to. Returns -1
4846 * if the source file descriptor is closed, EMPTY if there are no unused
4847 * file descriptors left.
4848 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004849/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4850 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004851enum {
4852 COPYFD_EXACT = (int)~(INT_MAX),
4853 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4854};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004855static int
4856copyfd(int from, int to)
4857{
4858 int newfd;
4859
Denis Vlasenko5a867312008-07-24 19:46:38 +00004860 if (to & COPYFD_EXACT) {
4861 to &= ~COPYFD_EXACT;
4862 /*if (from != to)*/
4863 newfd = dup2(from, to);
4864 } else {
4865 newfd = fcntl(from, F_DUPFD, to);
4866 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004867 if (newfd < 0) {
4868 if (errno == EMFILE)
4869 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004870 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004871 ash_msg_and_raise_error("%d: %m", from);
4872 }
4873 return newfd;
4874}
4875
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004876/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004877struct two_fd_t {
4878 int orig, copy;
4879};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004880struct redirtab {
4881 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004882 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004883 int pair_count;
4884 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004885};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004886#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004887
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004888static int need_to_remember(struct redirtab *rp, int fd)
4889{
4890 int i;
4891
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004892 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004893 return 0;
4894
4895 for (i = 0; i < rp->pair_count; i++) {
4896 if (rp->two_fd[i].orig == fd) {
4897 /* already remembered */
4898 return 0;
4899 }
4900 }
4901 return 1;
4902}
4903
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004904/* "hidden" fd is a fd used to read scripts, or a copy of such */
4905static int is_hidden_fd(struct redirtab *rp, int fd)
4906{
4907 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004908 struct parsefile *pf;
4909
4910 if (fd == -1)
4911 return 0;
4912 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004913 while (pf) {
4914 if (fd == pf->fd) {
4915 return 1;
4916 }
4917 pf = pf->prev;
4918 }
4919 if (!rp)
4920 return 0;
4921 fd |= COPYFD_RESTORE;
4922 for (i = 0; i < rp->pair_count; i++) {
4923 if (rp->two_fd[i].copy == fd) {
4924 return 1;
4925 }
4926 }
4927 return 0;
4928}
4929
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004930/*
4931 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4932 * old file descriptors are stashed away so that the redirection can be
4933 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4934 * standard output, and the standard error if it becomes a duplicate of
4935 * stdout, is saved in memory.
4936 */
4937/* flags passed to redirect */
4938#define REDIR_PUSH 01 /* save previous values of file descriptors */
4939#define REDIR_SAVEFD2 03 /* set preverrout */
4940static void
4941redirect(union node *redir, int flags)
4942{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004943 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004944 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004945 int i;
4946 int fd;
4947 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004948 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004949
Denis Vlasenko01631112007-12-16 17:20:38 +00004950 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004951 if (!redir) {
4952 return;
4953 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004954
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004955 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004956 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004957 INT_OFF;
4958 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004959 union node *tmp = redir;
4960 do {
4961 sv_pos++;
4962 tmp = tmp->nfile.next;
4963 } while (tmp);
4964 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004965 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004966 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004967 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004968 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004969 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004970 while (sv_pos > 0) {
4971 sv_pos--;
4972 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
4973 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004974 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004975
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004976 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00004977 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004978 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004979 int right_fd = redir->ndup.dupfd;
4980 /* redirect from/to same file descriptor? */
4981 if (right_fd == fd)
4982 continue;
4983 /* echo >&10 and 10 is a fd opened to the sh script? */
4984 if (is_hidden_fd(sv, right_fd)) {
4985 errno = EBADF; /* as if it is closed */
4986 ash_msg_and_raise_error("%d: %m", right_fd);
4987 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00004988 newfd = -1;
4989 } else {
4990 newfd = openredirect(redir); /* always >= 0 */
4991 if (fd == newfd) {
4992 /* Descriptor wasn't open before redirect.
4993 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004994 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00004995 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004996 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00004997 continue;
4998 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004999 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005000 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005001 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005002 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005003/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5004 * are closed in popredir() in the child, preventing them from leaking
5005 * into child. (popredir() also cleans up the mess in case of failures)
5006 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005007 if (i == -1) {
5008 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005009 if (i != EBADF) {
5010 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005011 if (newfd >= 0)
5012 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005013 errno = i;
5014 ash_msg_and_raise_error("%d: %m", fd);
5015 /* NOTREACHED */
5016 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005017 /* EBADF: it is not open - good, remember to close it */
5018 remember_to_close:
5019 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005020 } else { /* fd is open, save its copy */
5021 /* "exec fd>&-" should not close fds
5022 * which point to script file(s).
5023 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005024 if (is_hidden_fd(sv, fd))
5025 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005026 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005027 if (fd == 2)
5028 copied_fd2 = i;
5029 sv->two_fd[sv_pos].orig = fd;
5030 sv->two_fd[sv_pos].copy = i;
5031 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005032 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005033 if (newfd < 0) {
5034 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005035 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005036 close(fd);
5037 } else {
5038 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005039 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005040 } else if (fd != newfd) { /* move newfd to fd */
5041 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005042 close(newfd);
5043 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005044 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005045
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005046 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005047 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5048 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005049}
5050
5051/*
5052 * Undo the effects of the last redirection.
5053 */
5054static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005055popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005056{
5057 struct redirtab *rp;
5058 int i;
5059
Denis Vlasenko01631112007-12-16 17:20:38 +00005060 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005061 return;
5062 INT_OFF;
5063 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005064 for (i = 0; i < rp->pair_count; i++) {
5065 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005066 int copy = rp->two_fd[i].copy;
5067 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005068 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005069 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005070 continue;
5071 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005072 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005073 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005074 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005075 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005076 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005077 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005078 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005079 }
5080 }
5081 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005082 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005083 free(rp);
5084 INT_ON;
5085}
5086
5087/*
5088 * Undo all redirections. Called on error or interrupt.
5089 */
5090
5091/*
5092 * Discard all saved file descriptors.
5093 */
5094static void
5095clearredir(int drop)
5096{
5097 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005098 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005099 if (!redirlist)
5100 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005101 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005102 }
5103}
5104
5105static int
5106redirectsafe(union node *redir, int flags)
5107{
5108 int err;
5109 volatile int saveint;
5110 struct jmploc *volatile savehandler = exception_handler;
5111 struct jmploc jmploc;
5112
5113 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005114 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5115 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005116 if (!err) {
5117 exception_handler = &jmploc;
5118 redirect(redir, flags);
5119 }
5120 exception_handler = savehandler;
5121 if (err && exception != EXERROR)
5122 longjmp(exception_handler->loc, 1);
5123 RESTORE_INT(saveint);
5124 return err;
5125}
5126
5127
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005128/* ============ Routines to expand arguments to commands
5129 *
5130 * We have to deal with backquotes, shell variables, and file metacharacters.
5131 */
5132
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005133#if ENABLE_ASH_MATH_SUPPORT_64
5134typedef int64_t arith_t;
5135#define arith_t_type long long
5136#else
5137typedef long arith_t;
5138#define arith_t_type long
5139#endif
5140
5141#if ENABLE_ASH_MATH_SUPPORT
5142static arith_t dash_arith(const char *);
5143static arith_t arith(const char *expr, int *perrcode);
5144#endif
5145
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005146/*
5147 * expandarg flags
5148 */
5149#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5150#define EXP_TILDE 0x2 /* do normal tilde expansion */
5151#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5152#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5153#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5154#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5155#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5156#define EXP_WORD 0x80 /* expand word in parameter expansion */
5157#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5158/*
5159 * _rmescape() flags
5160 */
5161#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5162#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5163#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5164#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5165#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5166
5167/*
5168 * Structure specifying which parts of the string should be searched
5169 * for IFS characters.
5170 */
5171struct ifsregion {
5172 struct ifsregion *next; /* next region in list */
5173 int begoff; /* offset of start of region */
5174 int endoff; /* offset of end of region */
5175 int nulonly; /* search for nul bytes only */
5176};
5177
5178struct arglist {
5179 struct strlist *list;
5180 struct strlist **lastp;
5181};
5182
5183/* output of current string */
5184static char *expdest;
5185/* list of back quote expressions */
5186static struct nodelist *argbackq;
5187/* first struct in list of ifs regions */
5188static struct ifsregion ifsfirst;
5189/* last struct in list */
5190static struct ifsregion *ifslastp;
5191/* holds expanded arg list */
5192static struct arglist exparg;
5193
5194/*
5195 * Our own itoa().
5196 */
5197static int
5198cvtnum(arith_t num)
5199{
5200 int len;
5201
5202 expdest = makestrspace(32, expdest);
5203#if ENABLE_ASH_MATH_SUPPORT_64
5204 len = fmtstr(expdest, 32, "%lld", (long long) num);
5205#else
5206 len = fmtstr(expdest, 32, "%ld", num);
5207#endif
5208 STADJUST(len, expdest);
5209 return len;
5210}
5211
5212static size_t
5213esclen(const char *start, const char *p)
5214{
5215 size_t esc = 0;
5216
5217 while (p > start && *--p == CTLESC) {
5218 esc++;
5219 }
5220 return esc;
5221}
5222
5223/*
5224 * Remove any CTLESC characters from a string.
5225 */
5226static char *
5227_rmescapes(char *str, int flag)
5228{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005229 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005230
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005231 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005232 unsigned inquotes;
5233 int notescaped;
5234 int globbing;
5235
5236 p = strpbrk(str, qchars);
5237 if (!p) {
5238 return str;
5239 }
5240 q = p;
5241 r = str;
5242 if (flag & RMESCAPE_ALLOC) {
5243 size_t len = p - str;
5244 size_t fulllen = len + strlen(p) + 1;
5245
5246 if (flag & RMESCAPE_GROW) {
5247 r = makestrspace(fulllen, expdest);
5248 } else if (flag & RMESCAPE_HEAP) {
5249 r = ckmalloc(fulllen);
5250 } else {
5251 r = stalloc(fulllen);
5252 }
5253 q = r;
5254 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005255 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005256 }
5257 }
5258 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5259 globbing = flag & RMESCAPE_GLOB;
5260 notescaped = globbing;
5261 while (*p) {
5262 if (*p == CTLQUOTEMARK) {
5263 inquotes = ~inquotes;
5264 p++;
5265 notescaped = globbing;
5266 continue;
5267 }
5268 if (*p == '\\') {
5269 /* naked back slash */
5270 notescaped = 0;
5271 goto copy;
5272 }
5273 if (*p == CTLESC) {
5274 p++;
5275 if (notescaped && inquotes && *p != '/') {
5276 *q++ = '\\';
5277 }
5278 }
5279 notescaped = globbing;
5280 copy:
5281 *q++ = *p++;
5282 }
5283 *q = '\0';
5284 if (flag & RMESCAPE_GROW) {
5285 expdest = r;
5286 STADJUST(q - r + 1, expdest);
5287 }
5288 return r;
5289}
5290#define rmescapes(p) _rmescapes((p), 0)
5291
5292#define pmatch(a, b) !fnmatch((a), (b), 0)
5293
5294/*
5295 * Prepare a pattern for a expmeta (internal glob(3)) call.
5296 *
5297 * Returns an stalloced string.
5298 */
5299static char *
5300preglob(const char *pattern, int quoted, int flag)
5301{
5302 flag |= RMESCAPE_GLOB;
5303 if (quoted) {
5304 flag |= RMESCAPE_QUOTED;
5305 }
5306 return _rmescapes((char *)pattern, flag);
5307}
5308
5309/*
5310 * Put a string on the stack.
5311 */
5312static void
5313memtodest(const char *p, size_t len, int syntax, int quotes)
5314{
5315 char *q = expdest;
5316
5317 q = makestrspace(len * 2, q);
5318
5319 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005320 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005321 if (!c)
5322 continue;
5323 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5324 USTPUTC(CTLESC, q);
5325 USTPUTC(c, q);
5326 }
5327
5328 expdest = q;
5329}
5330
5331static void
5332strtodest(const char *p, int syntax, int quotes)
5333{
5334 memtodest(p, strlen(p), syntax, quotes);
5335}
5336
5337/*
5338 * Record the fact that we have to scan this region of the
5339 * string for IFS characters.
5340 */
5341static void
5342recordregion(int start, int end, int nulonly)
5343{
5344 struct ifsregion *ifsp;
5345
5346 if (ifslastp == NULL) {
5347 ifsp = &ifsfirst;
5348 } else {
5349 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005350 ifsp = ckzalloc(sizeof(*ifsp));
5351 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005352 ifslastp->next = ifsp;
5353 INT_ON;
5354 }
5355 ifslastp = ifsp;
5356 ifslastp->begoff = start;
5357 ifslastp->endoff = end;
5358 ifslastp->nulonly = nulonly;
5359}
5360
5361static void
5362removerecordregions(int endoff)
5363{
5364 if (ifslastp == NULL)
5365 return;
5366
5367 if (ifsfirst.endoff > endoff) {
5368 while (ifsfirst.next != NULL) {
5369 struct ifsregion *ifsp;
5370 INT_OFF;
5371 ifsp = ifsfirst.next->next;
5372 free(ifsfirst.next);
5373 ifsfirst.next = ifsp;
5374 INT_ON;
5375 }
5376 if (ifsfirst.begoff > endoff)
5377 ifslastp = NULL;
5378 else {
5379 ifslastp = &ifsfirst;
5380 ifsfirst.endoff = endoff;
5381 }
5382 return;
5383 }
5384
5385 ifslastp = &ifsfirst;
5386 while (ifslastp->next && ifslastp->next->begoff < endoff)
5387 ifslastp=ifslastp->next;
5388 while (ifslastp->next != NULL) {
5389 struct ifsregion *ifsp;
5390 INT_OFF;
5391 ifsp = ifslastp->next->next;
5392 free(ifslastp->next);
5393 ifslastp->next = ifsp;
5394 INT_ON;
5395 }
5396 if (ifslastp->endoff > endoff)
5397 ifslastp->endoff = endoff;
5398}
5399
5400static char *
5401exptilde(char *startp, char *p, int flag)
5402{
5403 char c;
5404 char *name;
5405 struct passwd *pw;
5406 const char *home;
5407 int quotes = flag & (EXP_FULL | EXP_CASE);
5408 int startloc;
5409
5410 name = p + 1;
5411
5412 while ((c = *++p) != '\0') {
5413 switch (c) {
5414 case CTLESC:
5415 return startp;
5416 case CTLQUOTEMARK:
5417 return startp;
5418 case ':':
5419 if (flag & EXP_VARTILDE)
5420 goto done;
5421 break;
5422 case '/':
5423 case CTLENDVAR:
5424 goto done;
5425 }
5426 }
5427 done:
5428 *p = '\0';
5429 if (*name == '\0') {
5430 home = lookupvar(homestr);
5431 } else {
5432 pw = getpwnam(name);
5433 if (pw == NULL)
5434 goto lose;
5435 home = pw->pw_dir;
5436 }
5437 if (!home || !*home)
5438 goto lose;
5439 *p = c;
5440 startloc = expdest - (char *)stackblock();
5441 strtodest(home, SQSYNTAX, quotes);
5442 recordregion(startloc, expdest - (char *)stackblock(), 0);
5443 return p;
5444 lose:
5445 *p = c;
5446 return startp;
5447}
5448
5449/*
5450 * Execute a command inside back quotes. If it's a builtin command, we
5451 * want to save its output in a block obtained from malloc. Otherwise
5452 * we fork off a subprocess and get the output of the command via a pipe.
5453 * Should be called with interrupts off.
5454 */
5455struct backcmd { /* result of evalbackcmd */
5456 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005457 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005458 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005459 struct job *jp; /* job structure for command */
5460};
5461
5462/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005463static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005464#define EV_EXIT 01 /* exit after evaluating tree */
5465static void evaltree(union node *, int);
5466
5467static void
5468evalbackcmd(union node *n, struct backcmd *result)
5469{
5470 int saveherefd;
5471
5472 result->fd = -1;
5473 result->buf = NULL;
5474 result->nleft = 0;
5475 result->jp = NULL;
5476 if (n == NULL) {
5477 goto out;
5478 }
5479
5480 saveherefd = herefd;
5481 herefd = -1;
5482
5483 {
5484 int pip[2];
5485 struct job *jp;
5486
5487 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005488 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005489 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005490 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5491 FORCE_INT_ON;
5492 close(pip[0]);
5493 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005494 /*close(1);*/
5495 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005496 close(pip[1]);
5497 }
5498 eflag = 0;
5499 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5500 /* NOTREACHED */
5501 }
5502 close(pip[1]);
5503 result->fd = pip[0];
5504 result->jp = jp;
5505 }
5506 herefd = saveherefd;
5507 out:
5508 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5509 result->fd, result->buf, result->nleft, result->jp));
5510}
5511
5512/*
5513 * Expand stuff in backwards quotes.
5514 */
5515static void
5516expbackq(union node *cmd, int quoted, int quotes)
5517{
5518 struct backcmd in;
5519 int i;
5520 char buf[128];
5521 char *p;
5522 char *dest;
5523 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005524 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005525 struct stackmark smark;
5526
5527 INT_OFF;
5528 setstackmark(&smark);
5529 dest = expdest;
5530 startloc = dest - (char *)stackblock();
5531 grabstackstr(dest);
5532 evalbackcmd(cmd, &in);
5533 popstackmark(&smark);
5534
5535 p = in.buf;
5536 i = in.nleft;
5537 if (i == 0)
5538 goto read;
5539 for (;;) {
5540 memtodest(p, i, syntax, quotes);
5541 read:
5542 if (in.fd < 0)
5543 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005544 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005545 TRACE(("expbackq: read returns %d\n", i));
5546 if (i <= 0)
5547 break;
5548 p = buf;
5549 }
5550
Denis Vlasenko60818682007-09-28 22:07:23 +00005551 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005552 if (in.fd >= 0) {
5553 close(in.fd);
5554 back_exitstatus = waitforjob(in.jp);
5555 }
5556 INT_ON;
5557
5558 /* Eat all trailing newlines */
5559 dest = expdest;
5560 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5561 STUNPUTC(dest);
5562 expdest = dest;
5563
5564 if (quoted == 0)
5565 recordregion(startloc, dest - (char *)stackblock(), 0);
5566 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5567 (dest - (char *)stackblock()) - startloc,
5568 (dest - (char *)stackblock()) - startloc,
5569 stackblock() + startloc));
5570}
5571
5572#if ENABLE_ASH_MATH_SUPPORT
5573/*
5574 * Expand arithmetic expression. Backup to start of expression,
5575 * evaluate, place result in (backed up) result, adjust string position.
5576 */
5577static void
5578expari(int quotes)
5579{
5580 char *p, *start;
5581 int begoff;
5582 int flag;
5583 int len;
5584
5585 /* ifsfree(); */
5586
5587 /*
5588 * This routine is slightly over-complicated for
5589 * efficiency. Next we scan backwards looking for the
5590 * start of arithmetic.
5591 */
5592 start = stackblock();
5593 p = expdest - 1;
5594 *p = '\0';
5595 p--;
5596 do {
5597 int esc;
5598
5599 while (*p != CTLARI) {
5600 p--;
5601#if DEBUG
5602 if (p < start) {
5603 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5604 }
5605#endif
5606 }
5607
5608 esc = esclen(start, p);
5609 if (!(esc % 2)) {
5610 break;
5611 }
5612
5613 p -= esc + 1;
5614 } while (1);
5615
5616 begoff = p - start;
5617
5618 removerecordregions(begoff);
5619
5620 flag = p[1];
5621
5622 expdest = p;
5623
5624 if (quotes)
5625 rmescapes(p + 2);
5626
5627 len = cvtnum(dash_arith(p + 2));
5628
5629 if (flag != '"')
5630 recordregion(begoff, begoff + len, 0);
5631}
5632#endif
5633
5634/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005635static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005636
5637/*
5638 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5639 * characters to allow for further processing. Otherwise treat
5640 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005641 *
5642 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5643 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5644 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005645 */
5646static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005647argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005649 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005650 '=',
5651 ':',
5652 CTLQUOTEMARK,
5653 CTLENDVAR,
5654 CTLESC,
5655 CTLVAR,
5656 CTLBACKQ,
5657 CTLBACKQ | CTLQUOTE,
5658#if ENABLE_ASH_MATH_SUPPORT
5659 CTLENDARI,
5660#endif
5661 0
5662 };
5663 const char *reject = spclchars;
5664 int c;
5665 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5666 int breakall = flag & EXP_WORD;
5667 int inquotes;
5668 size_t length;
5669 int startloc;
5670
5671 if (!(flag & EXP_VARTILDE)) {
5672 reject += 2;
5673 } else if (flag & EXP_VARTILDE2) {
5674 reject++;
5675 }
5676 inquotes = 0;
5677 length = 0;
5678 if (flag & EXP_TILDE) {
5679 char *q;
5680
5681 flag &= ~EXP_TILDE;
5682 tilde:
5683 q = p;
5684 if (*q == CTLESC && (flag & EXP_QWORD))
5685 q++;
5686 if (*q == '~')
5687 p = exptilde(p, q, flag);
5688 }
5689 start:
5690 startloc = expdest - (char *)stackblock();
5691 for (;;) {
5692 length += strcspn(p + length, reject);
5693 c = p[length];
5694 if (c && (!(c & 0x80)
5695#if ENABLE_ASH_MATH_SUPPORT
5696 || c == CTLENDARI
5697#endif
5698 )) {
5699 /* c == '=' || c == ':' || c == CTLENDARI */
5700 length++;
5701 }
5702 if (length > 0) {
5703 int newloc;
5704 expdest = stack_nputstr(p, length, expdest);
5705 newloc = expdest - (char *)stackblock();
5706 if (breakall && !inquotes && newloc > startloc) {
5707 recordregion(startloc, newloc, 0);
5708 }
5709 startloc = newloc;
5710 }
5711 p += length + 1;
5712 length = 0;
5713
5714 switch (c) {
5715 case '\0':
5716 goto breakloop;
5717 case '=':
5718 if (flag & EXP_VARTILDE2) {
5719 p--;
5720 continue;
5721 }
5722 flag |= EXP_VARTILDE2;
5723 reject++;
5724 /* fall through */
5725 case ':':
5726 /*
5727 * sort of a hack - expand tildes in variable
5728 * assignments (after the first '=' and after ':'s).
5729 */
5730 if (*--p == '~') {
5731 goto tilde;
5732 }
5733 continue;
5734 }
5735
5736 switch (c) {
5737 case CTLENDVAR: /* ??? */
5738 goto breakloop;
5739 case CTLQUOTEMARK:
5740 /* "$@" syntax adherence hack */
5741 if (
5742 !inquotes &&
5743 !memcmp(p, dolatstr, 4) &&
5744 (p[4] == CTLQUOTEMARK || (
5745 p[4] == CTLENDVAR &&
5746 p[5] == CTLQUOTEMARK
5747 ))
5748 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005749 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005750 goto start;
5751 }
5752 inquotes = !inquotes;
5753 addquote:
5754 if (quotes) {
5755 p--;
5756 length++;
5757 startloc++;
5758 }
5759 break;
5760 case CTLESC:
5761 startloc++;
5762 length++;
5763 goto addquote;
5764 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005765 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005766 goto start;
5767 case CTLBACKQ:
5768 c = 0;
5769 case CTLBACKQ|CTLQUOTE:
5770 expbackq(argbackq->n, c, quotes);
5771 argbackq = argbackq->next;
5772 goto start;
5773#if ENABLE_ASH_MATH_SUPPORT
5774 case CTLENDARI:
5775 p--;
5776 expari(quotes);
5777 goto start;
5778#endif
5779 }
5780 }
5781 breakloop:
5782 ;
5783}
5784
5785static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005786scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005787 int zero)
5788{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005789// This commented out code was added by James Simmons <jsimmons@infradead.org>
5790// as part of a larger change when he added support for ${var/a/b}.
5791// However, it broke # and % operators:
5792//
5793//var=ababcdcd
5794// ok bad
5795//echo ${var#ab} abcdcd abcdcd
5796//echo ${var##ab} abcdcd abcdcd
5797//echo ${var#a*b} abcdcd ababcdcd (!)
5798//echo ${var##a*b} cdcd cdcd
5799//echo ${var#?} babcdcd ababcdcd (!)
5800//echo ${var##?} babcdcd babcdcd
5801//echo ${var#*} ababcdcd babcdcd (!)
5802//echo ${var##*}
5803//echo ${var%cd} ababcd ababcd
5804//echo ${var%%cd} ababcd abab (!)
5805//echo ${var%c*d} ababcd ababcd
5806//echo ${var%%c*d} abab ababcdcd (!)
5807//echo ${var%?} ababcdc ababcdc
5808//echo ${var%%?} ababcdc ababcdcd (!)
5809//echo ${var%*} ababcdcd ababcdcd
5810//echo ${var%%*}
5811//
5812// Commenting it back out helped. Remove it completely if it really
5813// is not needed.
5814
5815 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005816 char c;
5817
5818 loc = startp;
5819 loc2 = rmesc;
5820 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005821 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005822 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005823
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005824 c = *loc2;
5825 if (zero) {
5826 *loc2 = '\0';
5827 s = rmesc;
5828 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005829 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005830
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005831// // chop off end if its '*'
5832// full = strrchr(str, '*');
5833// if (full && full != str)
5834// match--;
5835//
5836// // If str starts with '*' replace with s.
5837// if ((*str == '*') && strlen(s) >= match) {
5838// full = xstrdup(s);
5839// strncpy(full+strlen(s)-match+1, str+1, match-1);
5840// } else
5841// full = xstrndup(str, match);
5842// match = strncmp(s, full, strlen(full));
5843// free(full);
5844//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005845 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005846 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005847 return loc;
5848 if (quotes && *loc == CTLESC)
5849 loc++;
5850 loc++;
5851 loc2++;
5852 } while (c);
5853 return 0;
5854}
5855
5856static char *
5857scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5858 int zero)
5859{
5860 int esc = 0;
5861 char *loc;
5862 char *loc2;
5863
5864 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5865 int match;
5866 char c = *loc2;
5867 const char *s = loc2;
5868 if (zero) {
5869 *loc2 = '\0';
5870 s = rmesc;
5871 }
5872 match = pmatch(str, s);
5873 *loc2 = c;
5874 if (match)
5875 return loc;
5876 loc--;
5877 if (quotes) {
5878 if (--esc < 0) {
5879 esc = esclen(startp, loc);
5880 }
5881 if (esc % 2) {
5882 esc--;
5883 loc--;
5884 }
5885 }
5886 }
5887 return 0;
5888}
5889
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005890static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005891static void
5892varunset(const char *end, const char *var, const char *umsg, int varflags)
5893{
5894 const char *msg;
5895 const char *tail;
5896
5897 tail = nullstr;
5898 msg = "parameter not set";
5899 if (umsg) {
5900 if (*end == CTLENDVAR) {
5901 if (varflags & VSNUL)
5902 tail = " or null";
5903 } else
5904 msg = umsg;
5905 }
5906 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5907}
5908
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005909#if ENABLE_ASH_BASH_COMPAT
5910static char *
5911parse_sub_pattern(char *arg, int inquotes)
5912{
5913 char *idx, *repl = NULL;
5914 unsigned char c;
5915
Denis Vlasenko2659c632008-06-14 06:04:59 +00005916 idx = arg;
5917 while (1) {
5918 c = *arg;
5919 if (!c)
5920 break;
5921 if (c == '/') {
5922 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005923 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005924 repl = idx + 1;
5925 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005926 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005927 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005928 *idx++ = c;
5929 if (!inquotes && c == '\\' && arg[1] == '\\')
5930 arg++; /* skip both \\, not just first one */
5931 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005932 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005933 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005934
5935 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005936}
5937#endif /* ENABLE_ASH_BASH_COMPAT */
5938
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005939static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005940subevalvar(char *p, char *str, int strloc, int subtype,
5941 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005942{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005943 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005944 char *startp;
5945 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005946 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005947 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5948 USE_ASH_BASH_COMPAT(char null = '\0';)
5949 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5950 int saveherefd = herefd;
5951 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005952 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005954
5955 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005956 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5957 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958 STPUTC('\0', expdest);
5959 herefd = saveherefd;
5960 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005961 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005962
5963 switch (subtype) {
5964 case VSASSIGN:
5965 setvar(str, startp, 0);
5966 amount = startp - expdest;
5967 STADJUST(amount, expdest);
5968 return startp;
5969
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005970#if ENABLE_ASH_BASH_COMPAT
5971 case VSSUBSTR:
5972 loc = str = stackblock() + strloc;
5973// TODO: number() instead? It does error checking...
5974 pos = atoi(loc);
5975 len = str - startp - 1;
5976
5977 /* *loc != '\0', guaranteed by parser */
5978 if (quotes) {
5979 char *ptr;
5980
5981 /* We must adjust the length by the number of escapes we find. */
5982 for (ptr = startp; ptr < (str - 1); ptr++) {
5983 if(*ptr == CTLESC) {
5984 len--;
5985 ptr++;
5986 }
5987 }
5988 }
5989 orig_len = len;
5990
5991 if (*loc++ == ':') {
5992// TODO: number() instead? It does error checking...
5993 len = atoi(loc);
5994 } else {
5995 len = orig_len;
5996 while (*loc && *loc != ':')
5997 loc++;
5998 if (*loc++ == ':')
5999// TODO: number() instead? It does error checking...
6000 len = atoi(loc);
6001 }
6002 if (pos >= orig_len) {
6003 pos = 0;
6004 len = 0;
6005 }
6006 if (len > (orig_len - pos))
6007 len = orig_len - pos;
6008
6009 for (str = startp; pos; str++, pos--) {
6010 if (quotes && *str == CTLESC)
6011 str++;
6012 }
6013 for (loc = startp; len; len--) {
6014 if (quotes && *str == CTLESC)
6015 *loc++ = *str++;
6016 *loc++ = *str++;
6017 }
6018 *loc = '\0';
6019 amount = loc - expdest;
6020 STADJUST(amount, expdest);
6021 return loc;
6022#endif
6023
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006024 case VSQUESTION:
6025 varunset(p, str, startp, varflags);
6026 /* NOTREACHED */
6027 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006028 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006029
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006030 /* We'll comeback here if we grow the stack while handling
6031 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6032 * stack will need rebasing, and we'll need to remove our work
6033 * areas each time
6034 */
6035 USE_ASH_BASH_COMPAT(restart:)
6036
6037 amount = expdest - ((char *)stackblock() + resetloc);
6038 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006039 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006040
6041 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006042 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 if (quotes) {
6044 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6045 if (rmesc != startp) {
6046 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006047 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 }
6049 }
6050 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006051 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006052 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006053 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006054
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006055#if ENABLE_ASH_BASH_COMPAT
6056 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6057 char *idx, *end, *restart_detect;
6058
6059 if(!repl) {
6060 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6061 if (!repl)
6062 repl = &null;
6063 }
6064
6065 /* If there's no pattern to match, return the expansion unmolested */
6066 if (*str == '\0')
6067 return 0;
6068
6069 len = 0;
6070 idx = startp;
6071 end = str - 1;
6072 while (idx < end) {
6073 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6074 if (!loc) {
6075 /* No match, advance */
6076 restart_detect = stackblock();
6077 STPUTC(*idx, expdest);
6078 if (quotes && *idx == CTLESC) {
6079 idx++;
6080 len++;
6081 STPUTC(*idx, expdest);
6082 }
6083 if (stackblock() != restart_detect)
6084 goto restart;
6085 idx++;
6086 len++;
6087 rmesc++;
6088 continue;
6089 }
6090
6091 if (subtype == VSREPLACEALL) {
6092 while (idx < loc) {
6093 if (quotes && *idx == CTLESC)
6094 idx++;
6095 idx++;
6096 rmesc++;
6097 }
6098 } else
6099 idx = loc;
6100
6101 for (loc = repl; *loc; loc++) {
6102 restart_detect = stackblock();
6103 STPUTC(*loc, expdest);
6104 if (stackblock() != restart_detect)
6105 goto restart;
6106 len++;
6107 }
6108
6109 if (subtype == VSREPLACE) {
6110 while (*idx) {
6111 restart_detect = stackblock();
6112 STPUTC(*idx, expdest);
6113 if (stackblock() != restart_detect)
6114 goto restart;
6115 len++;
6116 idx++;
6117 }
6118 break;
6119 }
6120 }
6121
6122 /* We've put the replaced text into a buffer at workloc, now
6123 * move it to the right place and adjust the stack.
6124 */
6125 startp = stackblock() + startloc;
6126 STPUTC('\0', expdest);
6127 memmove(startp, stackblock() + workloc, len);
6128 startp[len++] = '\0';
6129 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6130 STADJUST(-amount, expdest);
6131 return startp;
6132 }
6133#endif /* ENABLE_ASH_BASH_COMPAT */
6134
6135 subtype -= VSTRIMRIGHT;
6136#if DEBUG
6137 if (subtype < 0 || subtype > 7)
6138 abort();
6139#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006140 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6141 zero = subtype >> 1;
6142 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6143 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6144
6145 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6146 if (loc) {
6147 if (zero) {
6148 memmove(startp, loc, str - loc);
6149 loc = startp + (str - loc) - 1;
6150 }
6151 *loc = '\0';
6152 amount = loc - expdest;
6153 STADJUST(amount, expdest);
6154 }
6155 return loc;
6156}
6157
6158/*
6159 * Add the value of a specialized variable to the stack string.
6160 */
6161static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006162varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006163{
6164 int num;
6165 char *p;
6166 int i;
6167 int sep = 0;
6168 int sepq = 0;
6169 ssize_t len = 0;
6170 char **ap;
6171 int syntax;
6172 int quoted = varflags & VSQUOTE;
6173 int subtype = varflags & VSTYPE;
6174 int quotes = flags & (EXP_FULL | EXP_CASE);
6175
6176 if (quoted && (flags & EXP_FULL))
6177 sep = 1 << CHAR_BIT;
6178
6179 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6180 switch (*name) {
6181 case '$':
6182 num = rootpid;
6183 goto numvar;
6184 case '?':
6185 num = exitstatus;
6186 goto numvar;
6187 case '#':
6188 num = shellparam.nparam;
6189 goto numvar;
6190 case '!':
6191 num = backgndpid;
6192 if (num == 0)
6193 return -1;
6194 numvar:
6195 len = cvtnum(num);
6196 break;
6197 case '-':
6198 p = makestrspace(NOPTS, expdest);
6199 for (i = NOPTS - 1; i >= 0; i--) {
6200 if (optlist[i]) {
6201 USTPUTC(optletters(i), p);
6202 len++;
6203 }
6204 }
6205 expdest = p;
6206 break;
6207 case '@':
6208 if (sep)
6209 goto param;
6210 /* fall through */
6211 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006212 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006213 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6214 sepq = 1;
6215 param:
6216 ap = shellparam.p;
6217 if (!ap)
6218 return -1;
6219 while ((p = *ap++)) {
6220 size_t partlen;
6221
6222 partlen = strlen(p);
6223 len += partlen;
6224
6225 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6226 memtodest(p, partlen, syntax, quotes);
6227
6228 if (*ap && sep) {
6229 char *q;
6230
6231 len++;
6232 if (subtype == VSPLUS || subtype == VSLENGTH) {
6233 continue;
6234 }
6235 q = expdest;
6236 if (sepq)
6237 STPUTC(CTLESC, q);
6238 STPUTC(sep, q);
6239 expdest = q;
6240 }
6241 }
6242 return len;
6243 case '0':
6244 case '1':
6245 case '2':
6246 case '3':
6247 case '4':
6248 case '5':
6249 case '6':
6250 case '7':
6251 case '8':
6252 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006253// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006254 num = atoi(name);
6255 if (num < 0 || num > shellparam.nparam)
6256 return -1;
6257 p = num ? shellparam.p[num - 1] : arg0;
6258 goto value;
6259 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006260 /* NB: name has form "VAR=..." */
6261
6262 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6263 * which should be considered before we check variables. */
6264 if (var_str_list) {
6265 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6266 p = NULL;
6267 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006268 char *str, *eq;
6269 str = var_str_list->text;
6270 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006271 if (!eq) /* stop at first non-assignment */
6272 break;
6273 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006274 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006275 && strncmp(str, name, name_len) == 0) {
6276 p = eq;
6277 /* goto value; - WRONG! */
6278 /* think "A=1 A=2 B=$A" */
6279 }
6280 var_str_list = var_str_list->next;
6281 } while (var_str_list);
6282 if (p)
6283 goto value;
6284 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006285 p = lookupvar(name);
6286 value:
6287 if (!p)
6288 return -1;
6289
6290 len = strlen(p);
6291 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6292 memtodest(p, len, syntax, quotes);
6293 return len;
6294 }
6295
6296 if (subtype == VSPLUS || subtype == VSLENGTH)
6297 STADJUST(-len, expdest);
6298 return len;
6299}
6300
6301/*
6302 * Expand a variable, and return a pointer to the next character in the
6303 * input string.
6304 */
6305static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006306evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006307{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006308 char varflags;
6309 char subtype;
6310 char quoted;
6311 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006312 char *var;
6313 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006314 int startloc;
6315 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006316
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006317 varflags = *p++;
6318 subtype = varflags & VSTYPE;
6319 quoted = varflags & VSQUOTE;
6320 var = p;
6321 easy = (!quoted || (*var == '@' && shellparam.nparam));
6322 startloc = expdest - (char *)stackblock();
6323 p = strchr(p, '=') + 1;
6324
6325 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006326 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006327 if (varflags & VSNUL)
6328 varlen--;
6329
6330 if (subtype == VSPLUS) {
6331 varlen = -1 - varlen;
6332 goto vsplus;
6333 }
6334
6335 if (subtype == VSMINUS) {
6336 vsplus:
6337 if (varlen < 0) {
6338 argstr(
6339 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006340 (quoted ? EXP_QWORD : EXP_WORD),
6341 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006342 );
6343 goto end;
6344 }
6345 if (easy)
6346 goto record;
6347 goto end;
6348 }
6349
6350 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6351 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006352 if (subevalvar(p, var, /* strloc: */ 0,
6353 subtype, startloc, varflags,
6354 /* quotes: */ 0,
6355 var_str_list)
6356 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006357 varflags &= ~VSNUL;
6358 /*
6359 * Remove any recorded regions beyond
6360 * start of variable
6361 */
6362 removerecordregions(startloc);
6363 goto again;
6364 }
6365 goto end;
6366 }
6367 if (easy)
6368 goto record;
6369 goto end;
6370 }
6371
6372 if (varlen < 0 && uflag)
6373 varunset(p, var, 0, 0);
6374
6375 if (subtype == VSLENGTH) {
6376 cvtnum(varlen > 0 ? varlen : 0);
6377 goto record;
6378 }
6379
6380 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006381 if (easy)
6382 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006383 goto end;
6384 }
6385
6386#if DEBUG
6387 switch (subtype) {
6388 case VSTRIMLEFT:
6389 case VSTRIMLEFTMAX:
6390 case VSTRIMRIGHT:
6391 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006392#if ENABLE_ASH_BASH_COMPAT
6393 case VSSUBSTR:
6394 case VSREPLACE:
6395 case VSREPLACEALL:
6396#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006397 break;
6398 default:
6399 abort();
6400 }
6401#endif
6402
6403 if (varlen >= 0) {
6404 /*
6405 * Terminate the string and start recording the pattern
6406 * right after it
6407 */
6408 STPUTC('\0', expdest);
6409 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006410 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6411 startloc, varflags,
6412 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6413 var_str_list)
6414 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006415 int amount = expdest - (
6416 (char *)stackblock() + patloc - 1
6417 );
6418 STADJUST(-amount, expdest);
6419 }
6420 /* Remove any recorded regions beyond start of variable */
6421 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006422 record:
6423 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006424 }
6425
6426 end:
6427 if (subtype != VSNORMAL) { /* skip to end of alternative */
6428 int nesting = 1;
6429 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006430 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006431 if (c == CTLESC)
6432 p++;
6433 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6434 if (varlen >= 0)
6435 argbackq = argbackq->next;
6436 } else if (c == CTLVAR) {
6437 if ((*p++ & VSTYPE) != VSNORMAL)
6438 nesting++;
6439 } else if (c == CTLENDVAR) {
6440 if (--nesting == 0)
6441 break;
6442 }
6443 }
6444 }
6445 return p;
6446}
6447
6448/*
6449 * Break the argument string into pieces based upon IFS and add the
6450 * strings to the argument list. The regions of the string to be
6451 * searched for IFS characters have been stored by recordregion.
6452 */
6453static void
6454ifsbreakup(char *string, struct arglist *arglist)
6455{
6456 struct ifsregion *ifsp;
6457 struct strlist *sp;
6458 char *start;
6459 char *p;
6460 char *q;
6461 const char *ifs, *realifs;
6462 int ifsspc;
6463 int nulonly;
6464
6465 start = string;
6466 if (ifslastp != NULL) {
6467 ifsspc = 0;
6468 nulonly = 0;
6469 realifs = ifsset() ? ifsval() : defifs;
6470 ifsp = &ifsfirst;
6471 do {
6472 p = string + ifsp->begoff;
6473 nulonly = ifsp->nulonly;
6474 ifs = nulonly ? nullstr : realifs;
6475 ifsspc = 0;
6476 while (p < string + ifsp->endoff) {
6477 q = p;
6478 if (*p == CTLESC)
6479 p++;
6480 if (!strchr(ifs, *p)) {
6481 p++;
6482 continue;
6483 }
6484 if (!nulonly)
6485 ifsspc = (strchr(defifs, *p) != NULL);
6486 /* Ignore IFS whitespace at start */
6487 if (q == start && ifsspc) {
6488 p++;
6489 start = p;
6490 continue;
6491 }
6492 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006493 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006494 sp->text = start;
6495 *arglist->lastp = sp;
6496 arglist->lastp = &sp->next;
6497 p++;
6498 if (!nulonly) {
6499 for (;;) {
6500 if (p >= string + ifsp->endoff) {
6501 break;
6502 }
6503 q = p;
6504 if (*p == CTLESC)
6505 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006506 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006507 p = q;
6508 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006509 }
6510 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006511 if (ifsspc) {
6512 p++;
6513 ifsspc = 0;
6514 } else {
6515 p = q;
6516 break;
6517 }
6518 } else
6519 p++;
6520 }
6521 }
6522 start = p;
6523 } /* while */
6524 ifsp = ifsp->next;
6525 } while (ifsp != NULL);
6526 if (nulonly)
6527 goto add;
6528 }
6529
6530 if (!*start)
6531 return;
6532
6533 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006534 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006535 sp->text = start;
6536 *arglist->lastp = sp;
6537 arglist->lastp = &sp->next;
6538}
6539
6540static void
6541ifsfree(void)
6542{
6543 struct ifsregion *p;
6544
6545 INT_OFF;
6546 p = ifsfirst.next;
6547 do {
6548 struct ifsregion *ifsp;
6549 ifsp = p->next;
6550 free(p);
6551 p = ifsp;
6552 } while (p);
6553 ifslastp = NULL;
6554 ifsfirst.next = NULL;
6555 INT_ON;
6556}
6557
6558/*
6559 * Add a file name to the list.
6560 */
6561static void
6562addfname(const char *name)
6563{
6564 struct strlist *sp;
6565
Denis Vlasenko597906c2008-02-20 16:38:54 +00006566 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006567 sp->text = ststrdup(name);
6568 *exparg.lastp = sp;
6569 exparg.lastp = &sp->next;
6570}
6571
6572static char *expdir;
6573
6574/*
6575 * Do metacharacter (i.e. *, ?, [...]) expansion.
6576 */
6577static void
6578expmeta(char *enddir, char *name)
6579{
6580 char *p;
6581 const char *cp;
6582 char *start;
6583 char *endname;
6584 int metaflag;
6585 struct stat statb;
6586 DIR *dirp;
6587 struct dirent *dp;
6588 int atend;
6589 int matchdot;
6590
6591 metaflag = 0;
6592 start = name;
6593 for (p = name; *p; p++) {
6594 if (*p == '*' || *p == '?')
6595 metaflag = 1;
6596 else if (*p == '[') {
6597 char *q = p + 1;
6598 if (*q == '!')
6599 q++;
6600 for (;;) {
6601 if (*q == '\\')
6602 q++;
6603 if (*q == '/' || *q == '\0')
6604 break;
6605 if (*++q == ']') {
6606 metaflag = 1;
6607 break;
6608 }
6609 }
6610 } else if (*p == '\\')
6611 p++;
6612 else if (*p == '/') {
6613 if (metaflag)
6614 goto out;
6615 start = p + 1;
6616 }
6617 }
6618 out:
6619 if (metaflag == 0) { /* we've reached the end of the file name */
6620 if (enddir != expdir)
6621 metaflag++;
6622 p = name;
6623 do {
6624 if (*p == '\\')
6625 p++;
6626 *enddir++ = *p;
6627 } while (*p++);
6628 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6629 addfname(expdir);
6630 return;
6631 }
6632 endname = p;
6633 if (name < start) {
6634 p = name;
6635 do {
6636 if (*p == '\\')
6637 p++;
6638 *enddir++ = *p++;
6639 } while (p < start);
6640 }
6641 if (enddir == expdir) {
6642 cp = ".";
6643 } else if (enddir == expdir + 1 && *expdir == '/') {
6644 cp = "/";
6645 } else {
6646 cp = expdir;
6647 enddir[-1] = '\0';
6648 }
6649 dirp = opendir(cp);
6650 if (dirp == NULL)
6651 return;
6652 if (enddir != expdir)
6653 enddir[-1] = '/';
6654 if (*endname == 0) {
6655 atend = 1;
6656 } else {
6657 atend = 0;
6658 *endname++ = '\0';
6659 }
6660 matchdot = 0;
6661 p = start;
6662 if (*p == '\\')
6663 p++;
6664 if (*p == '.')
6665 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006666 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006667 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006668 continue;
6669 if (pmatch(start, dp->d_name)) {
6670 if (atend) {
6671 strcpy(enddir, dp->d_name);
6672 addfname(expdir);
6673 } else {
6674 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6675 continue;
6676 p[-1] = '/';
6677 expmeta(p, endname);
6678 }
6679 }
6680 }
6681 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006682 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006683 endname[-1] = '/';
6684}
6685
6686static struct strlist *
6687msort(struct strlist *list, int len)
6688{
6689 struct strlist *p, *q = NULL;
6690 struct strlist **lpp;
6691 int half;
6692 int n;
6693
6694 if (len <= 1)
6695 return list;
6696 half = len >> 1;
6697 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006698 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006699 q = p;
6700 p = p->next;
6701 }
6702 q->next = NULL; /* terminate first half of list */
6703 q = msort(list, half); /* sort first half of list */
6704 p = msort(p, len - half); /* sort second half */
6705 lpp = &list;
6706 for (;;) {
6707#if ENABLE_LOCALE_SUPPORT
6708 if (strcoll(p->text, q->text) < 0)
6709#else
6710 if (strcmp(p->text, q->text) < 0)
6711#endif
6712 {
6713 *lpp = p;
6714 lpp = &p->next;
6715 p = *lpp;
6716 if (p == NULL) {
6717 *lpp = q;
6718 break;
6719 }
6720 } else {
6721 *lpp = q;
6722 lpp = &q->next;
6723 q = *lpp;
6724 if (q == NULL) {
6725 *lpp = p;
6726 break;
6727 }
6728 }
6729 }
6730 return list;
6731}
6732
6733/*
6734 * Sort the results of file name expansion. It calculates the number of
6735 * strings to sort and then calls msort (short for merge sort) to do the
6736 * work.
6737 */
6738static struct strlist *
6739expsort(struct strlist *str)
6740{
6741 int len;
6742 struct strlist *sp;
6743
6744 len = 0;
6745 for (sp = str; sp; sp = sp->next)
6746 len++;
6747 return msort(str, len);
6748}
6749
6750static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006751expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006752{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006753 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006754 '*', '?', '[', 0
6755 };
6756 /* TODO - EXP_REDIR */
6757
6758 while (str) {
6759 struct strlist **savelastp;
6760 struct strlist *sp;
6761 char *p;
6762
6763 if (fflag)
6764 goto nometa;
6765 if (!strpbrk(str->text, metachars))
6766 goto nometa;
6767 savelastp = exparg.lastp;
6768
6769 INT_OFF;
6770 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6771 {
6772 int i = strlen(str->text);
6773 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6774 }
6775
6776 expmeta(expdir, p);
6777 free(expdir);
6778 if (p != str->text)
6779 free(p);
6780 INT_ON;
6781 if (exparg.lastp == savelastp) {
6782 /*
6783 * no matches
6784 */
6785 nometa:
6786 *exparg.lastp = str;
6787 rmescapes(str->text);
6788 exparg.lastp = &str->next;
6789 } else {
6790 *exparg.lastp = NULL;
6791 *savelastp = sp = expsort(*savelastp);
6792 while (sp->next != NULL)
6793 sp = sp->next;
6794 exparg.lastp = &sp->next;
6795 }
6796 str = str->next;
6797 }
6798}
6799
6800/*
6801 * Perform variable substitution and command substitution on an argument,
6802 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6803 * perform splitting and file name expansion. When arglist is NULL, perform
6804 * here document expansion.
6805 */
6806static void
6807expandarg(union node *arg, struct arglist *arglist, int flag)
6808{
6809 struct strlist *sp;
6810 char *p;
6811
6812 argbackq = arg->narg.backquote;
6813 STARTSTACKSTR(expdest);
6814 ifsfirst.next = NULL;
6815 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006816 argstr(arg->narg.text, flag,
6817 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006818 p = _STPUTC('\0', expdest);
6819 expdest = p - 1;
6820 if (arglist == NULL) {
6821 return; /* here document expanded */
6822 }
6823 p = grabstackstr(p);
6824 exparg.lastp = &exparg.list;
6825 /*
6826 * TODO - EXP_REDIR
6827 */
6828 if (flag & EXP_FULL) {
6829 ifsbreakup(p, &exparg);
6830 *exparg.lastp = NULL;
6831 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006832 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006833 } else {
6834 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6835 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006836 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006837 sp->text = p;
6838 *exparg.lastp = sp;
6839 exparg.lastp = &sp->next;
6840 }
6841 if (ifsfirst.next)
6842 ifsfree();
6843 *exparg.lastp = NULL;
6844 if (exparg.list) {
6845 *arglist->lastp = exparg.list;
6846 arglist->lastp = exparg.lastp;
6847 }
6848}
6849
6850/*
6851 * Expand shell variables and backquotes inside a here document.
6852 */
6853static void
6854expandhere(union node *arg, int fd)
6855{
6856 herefd = fd;
6857 expandarg(arg, (struct arglist *)NULL, 0);
6858 full_write(fd, stackblock(), expdest - (char *)stackblock());
6859}
6860
6861/*
6862 * Returns true if the pattern matches the string.
6863 */
6864static int
6865patmatch(char *pattern, const char *string)
6866{
6867 return pmatch(preglob(pattern, 0, 0), string);
6868}
6869
6870/*
6871 * See if a pattern matches in a case statement.
6872 */
6873static int
6874casematch(union node *pattern, char *val)
6875{
6876 struct stackmark smark;
6877 int result;
6878
6879 setstackmark(&smark);
6880 argbackq = pattern->narg.backquote;
6881 STARTSTACKSTR(expdest);
6882 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006883 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6884 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006885 STACKSTRNUL(expdest);
6886 result = patmatch(stackblock(), val);
6887 popstackmark(&smark);
6888 return result;
6889}
6890
6891
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006892/* ============ find_command */
6893
6894struct builtincmd {
6895 const char *name;
6896 int (*builtin)(int, char **);
6897 /* unsigned flags; */
6898};
6899#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006900/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006901 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006902#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006903#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006904
6905struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006906 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006907 union param {
6908 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006909 /* index >= 0 for commands without path (slashes) */
6910 /* (TODO: what exactly does the value mean? PATH position?) */
6911 /* index == -1 for commands with slashes */
6912 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006913 const struct builtincmd *cmd;
6914 struct funcnode *func;
6915 } u;
6916};
6917/* values of cmdtype */
6918#define CMDUNKNOWN -1 /* no entry in table for command */
6919#define CMDNORMAL 0 /* command is an executable program */
6920#define CMDFUNCTION 1 /* command is a shell function */
6921#define CMDBUILTIN 2 /* command is a shell builtin */
6922
6923/* action to find_command() */
6924#define DO_ERR 0x01 /* prints errors */
6925#define DO_ABS 0x02 /* checks absolute paths */
6926#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6927#define DO_ALTPATH 0x08 /* using alternate path */
6928#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6929
6930static void find_command(char *, struct cmdentry *, int, const char *);
6931
6932
6933/* ============ Hashing commands */
6934
6935/*
6936 * When commands are first encountered, they are entered in a hash table.
6937 * This ensures that a full path search will not have to be done for them
6938 * on each invocation.
6939 *
6940 * We should investigate converting to a linear search, even though that
6941 * would make the command name "hash" a misnomer.
6942 */
6943
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006944struct tblentry {
6945 struct tblentry *next; /* next entry in hash chain */
6946 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006947 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006948 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006949 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006950};
6951
Denis Vlasenko01631112007-12-16 17:20:38 +00006952static struct tblentry **cmdtable;
6953#define INIT_G_cmdtable() do { \
6954 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6955} while (0)
6956
6957static int builtinloc = -1; /* index in path of %builtin, or -1 */
6958
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006959
6960static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006961tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006962{
6963 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006964
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006965#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006966 if (applet_no >= 0) {
6967 if (APPLET_IS_NOEXEC(applet_no))
6968 run_applet_no_and_exit(applet_no, argv);
6969 /* re-exec ourselves with the new arguments */
6970 execve(bb_busybox_exec_path, argv, envp);
6971 /* If they called chroot or otherwise made the binary no longer
6972 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006973 }
6974#endif
6975
6976 repeat:
6977#ifdef SYSV
6978 do {
6979 execve(cmd, argv, envp);
6980 } while (errno == EINTR);
6981#else
6982 execve(cmd, argv, envp);
6983#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006984 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006985 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006986 return;
6987 }
6988 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006989 char **ap;
6990 char **new;
6991
6992 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006993 continue;
6994 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006995 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006996 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006997 ap += 2;
6998 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006999 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007000 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007001 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007002 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007003 goto repeat;
7004 }
7005}
7006
7007/*
7008 * Exec a program. Never returns. If you change this routine, you may
7009 * have to change the find_command routine as well.
7010 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007011static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007012static void
7013shellexec(char **argv, const char *path, int idx)
7014{
7015 char *cmdname;
7016 int e;
7017 char **envp;
7018 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007019#if ENABLE_FEATURE_SH_STANDALONE
7020 int applet_no = -1;
7021#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007022
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007023 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007024 envp = listvars(VEXPORT, VUNSET, 0);
7025 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007026#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007027 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007028#endif
7029 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007030 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007031 e = errno;
7032 } else {
7033 e = ENOENT;
7034 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7035 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007036 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007037 if (errno != ENOENT && errno != ENOTDIR)
7038 e = errno;
7039 }
7040 stunalloc(cmdname);
7041 }
7042 }
7043
7044 /* Map to POSIX errors */
7045 switch (e) {
7046 case EACCES:
7047 exerrno = 126;
7048 break;
7049 case ENOENT:
7050 exerrno = 127;
7051 break;
7052 default:
7053 exerrno = 2;
7054 break;
7055 }
7056 exitstatus = exerrno;
7057 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007058 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007059 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7060 /* NOTREACHED */
7061}
7062
7063static void
7064printentry(struct tblentry *cmdp)
7065{
7066 int idx;
7067 const char *path;
7068 char *name;
7069
7070 idx = cmdp->param.index;
7071 path = pathval();
7072 do {
7073 name = padvance(&path, cmdp->cmdname);
7074 stunalloc(name);
7075 } while (--idx >= 0);
7076 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7077}
7078
7079/*
7080 * Clear out command entries. The argument specifies the first entry in
7081 * PATH which has changed.
7082 */
7083static void
7084clearcmdentry(int firstchange)
7085{
7086 struct tblentry **tblp;
7087 struct tblentry **pp;
7088 struct tblentry *cmdp;
7089
7090 INT_OFF;
7091 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7092 pp = tblp;
7093 while ((cmdp = *pp) != NULL) {
7094 if ((cmdp->cmdtype == CMDNORMAL &&
7095 cmdp->param.index >= firstchange)
7096 || (cmdp->cmdtype == CMDBUILTIN &&
7097 builtinloc >= firstchange)
7098 ) {
7099 *pp = cmdp->next;
7100 free(cmdp);
7101 } else {
7102 pp = &cmdp->next;
7103 }
7104 }
7105 }
7106 INT_ON;
7107}
7108
7109/*
7110 * Locate a command in the command hash table. If "add" is nonzero,
7111 * add the command to the table if it is not already present. The
7112 * variable "lastcmdentry" is set to point to the address of the link
7113 * pointing to the entry, so that delete_cmd_entry can delete the
7114 * entry.
7115 *
7116 * Interrupts must be off if called with add != 0.
7117 */
7118static struct tblentry **lastcmdentry;
7119
7120static struct tblentry *
7121cmdlookup(const char *name, int add)
7122{
7123 unsigned int hashval;
7124 const char *p;
7125 struct tblentry *cmdp;
7126 struct tblentry **pp;
7127
7128 p = name;
7129 hashval = (unsigned char)*p << 4;
7130 while (*p)
7131 hashval += (unsigned char)*p++;
7132 hashval &= 0x7FFF;
7133 pp = &cmdtable[hashval % CMDTABLESIZE];
7134 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7135 if (strcmp(cmdp->cmdname, name) == 0)
7136 break;
7137 pp = &cmdp->next;
7138 }
7139 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007140 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7141 + strlen(name)
7142 /* + 1 - already done because
7143 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007144 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007145 cmdp->cmdtype = CMDUNKNOWN;
7146 strcpy(cmdp->cmdname, name);
7147 }
7148 lastcmdentry = pp;
7149 return cmdp;
7150}
7151
7152/*
7153 * Delete the command entry returned on the last lookup.
7154 */
7155static void
7156delete_cmd_entry(void)
7157{
7158 struct tblentry *cmdp;
7159
7160 INT_OFF;
7161 cmdp = *lastcmdentry;
7162 *lastcmdentry = cmdp->next;
7163 if (cmdp->cmdtype == CMDFUNCTION)
7164 freefunc(cmdp->param.func);
7165 free(cmdp);
7166 INT_ON;
7167}
7168
7169/*
7170 * Add a new command entry, replacing any existing command entry for
7171 * the same name - except special builtins.
7172 */
7173static void
7174addcmdentry(char *name, struct cmdentry *entry)
7175{
7176 struct tblentry *cmdp;
7177
7178 cmdp = cmdlookup(name, 1);
7179 if (cmdp->cmdtype == CMDFUNCTION) {
7180 freefunc(cmdp->param.func);
7181 }
7182 cmdp->cmdtype = entry->cmdtype;
7183 cmdp->param = entry->u;
7184 cmdp->rehash = 0;
7185}
7186
7187static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007188hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007189{
7190 struct tblentry **pp;
7191 struct tblentry *cmdp;
7192 int c;
7193 struct cmdentry entry;
7194 char *name;
7195
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007196 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007197 clearcmdentry(0);
7198 return 0;
7199 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007200
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007201 if (*argptr == NULL) {
7202 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7203 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7204 if (cmdp->cmdtype == CMDNORMAL)
7205 printentry(cmdp);
7206 }
7207 }
7208 return 0;
7209 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007210
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007211 c = 0;
7212 while ((name = *argptr) != NULL) {
7213 cmdp = cmdlookup(name, 0);
7214 if (cmdp != NULL
7215 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007216 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7217 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007218 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007219 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007220 find_command(name, &entry, DO_ERR, pathval());
7221 if (entry.cmdtype == CMDUNKNOWN)
7222 c = 1;
7223 argptr++;
7224 }
7225 return c;
7226}
7227
7228/*
7229 * Called when a cd is done. Marks all commands so the next time they
7230 * are executed they will be rehashed.
7231 */
7232static void
7233hashcd(void)
7234{
7235 struct tblentry **pp;
7236 struct tblentry *cmdp;
7237
7238 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7239 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007240 if (cmdp->cmdtype == CMDNORMAL
7241 || (cmdp->cmdtype == CMDBUILTIN
7242 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7243 && builtinloc > 0)
7244 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007245 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007246 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007247 }
7248 }
7249}
7250
7251/*
7252 * Fix command hash table when PATH changed.
7253 * Called before PATH is changed. The argument is the new value of PATH;
7254 * pathval() still returns the old value at this point.
7255 * Called with interrupts off.
7256 */
7257static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007258changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007259{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007260 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007261 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007262 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007263 int idx_bltin;
7264
7265 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007266 firstchange = 9999; /* assume no change */
7267 idx = 0;
7268 idx_bltin = -1;
7269 for (;;) {
7270 if (*old != *new) {
7271 firstchange = idx;
7272 if ((*old == '\0' && *new == ':')
7273 || (*old == ':' && *new == '\0'))
7274 firstchange++;
7275 old = new; /* ignore subsequent differences */
7276 }
7277 if (*new == '\0')
7278 break;
7279 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7280 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007281 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007282 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007283 new++, old++;
7284 }
7285 if (builtinloc < 0 && idx_bltin >= 0)
7286 builtinloc = idx_bltin; /* zap builtins */
7287 if (builtinloc >= 0 && idx_bltin < 0)
7288 firstchange = 0;
7289 clearcmdentry(firstchange);
7290 builtinloc = idx_bltin;
7291}
7292
7293#define TEOF 0
7294#define TNL 1
7295#define TREDIR 2
7296#define TWORD 3
7297#define TSEMI 4
7298#define TBACKGND 5
7299#define TAND 6
7300#define TOR 7
7301#define TPIPE 8
7302#define TLP 9
7303#define TRP 10
7304#define TENDCASE 11
7305#define TENDBQUOTE 12
7306#define TNOT 13
7307#define TCASE 14
7308#define TDO 15
7309#define TDONE 16
7310#define TELIF 17
7311#define TELSE 18
7312#define TESAC 19
7313#define TFI 20
7314#define TFOR 21
7315#define TIF 22
7316#define TIN 23
7317#define TTHEN 24
7318#define TUNTIL 25
7319#define TWHILE 26
7320#define TBEGIN 27
7321#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007322typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007323
7324/* first char is indicating which tokens mark the end of a list */
7325static const char *const tokname_array[] = {
7326 "\1end of file",
7327 "\0newline",
7328 "\0redirection",
7329 "\0word",
7330 "\0;",
7331 "\0&",
7332 "\0&&",
7333 "\0||",
7334 "\0|",
7335 "\0(",
7336 "\1)",
7337 "\1;;",
7338 "\1`",
7339#define KWDOFFSET 13
7340 /* the following are keywords */
7341 "\0!",
7342 "\0case",
7343 "\1do",
7344 "\1done",
7345 "\1elif",
7346 "\1else",
7347 "\1esac",
7348 "\1fi",
7349 "\0for",
7350 "\0if",
7351 "\0in",
7352 "\1then",
7353 "\0until",
7354 "\0while",
7355 "\0{",
7356 "\1}",
7357};
7358
7359static const char *
7360tokname(int tok)
7361{
7362 static char buf[16];
7363
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007364//try this:
7365//if (tok < TSEMI) return tokname_array[tok] + 1;
7366//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7367//return buf;
7368
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007369 if (tok >= TSEMI)
7370 buf[0] = '"';
7371 sprintf(buf + (tok >= TSEMI), "%s%c",
7372 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7373 return buf;
7374}
7375
7376/* Wrapper around strcmp for qsort/bsearch/... */
7377static int
7378pstrcmp(const void *a, const void *b)
7379{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007380 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007381}
7382
7383static const char *const *
7384findkwd(const char *s)
7385{
7386 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007387 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7388 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007389}
7390
7391/*
7392 * Locate and print what a word is...
7393 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007394static int
7395describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007396{
7397 struct cmdentry entry;
7398 struct tblentry *cmdp;
7399#if ENABLE_ASH_ALIAS
7400 const struct alias *ap;
7401#endif
7402 const char *path = pathval();
7403
7404 if (describe_command_verbose) {
7405 out1str(command);
7406 }
7407
7408 /* First look at the keywords */
7409 if (findkwd(command)) {
7410 out1str(describe_command_verbose ? " is a shell keyword" : command);
7411 goto out;
7412 }
7413
7414#if ENABLE_ASH_ALIAS
7415 /* Then look at the aliases */
7416 ap = lookupalias(command, 0);
7417 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007418 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007419 out1str("alias ");
7420 printalias(ap);
7421 return 0;
7422 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007423 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007424 goto out;
7425 }
7426#endif
7427 /* Then check if it is a tracked alias */
7428 cmdp = cmdlookup(command, 0);
7429 if (cmdp != NULL) {
7430 entry.cmdtype = cmdp->cmdtype;
7431 entry.u = cmdp->param;
7432 } else {
7433 /* Finally use brute force */
7434 find_command(command, &entry, DO_ABS, path);
7435 }
7436
7437 switch (entry.cmdtype) {
7438 case CMDNORMAL: {
7439 int j = entry.u.index;
7440 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007441 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007442 p = command;
7443 } else {
7444 do {
7445 p = padvance(&path, command);
7446 stunalloc(p);
7447 } while (--j >= 0);
7448 }
7449 if (describe_command_verbose) {
7450 out1fmt(" is%s %s",
7451 (cmdp ? " a tracked alias for" : nullstr), p
7452 );
7453 } else {
7454 out1str(p);
7455 }
7456 break;
7457 }
7458
7459 case CMDFUNCTION:
7460 if (describe_command_verbose) {
7461 out1str(" is a shell function");
7462 } else {
7463 out1str(command);
7464 }
7465 break;
7466
7467 case CMDBUILTIN:
7468 if (describe_command_verbose) {
7469 out1fmt(" is a %sshell builtin",
7470 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7471 "special " : nullstr
7472 );
7473 } else {
7474 out1str(command);
7475 }
7476 break;
7477
7478 default:
7479 if (describe_command_verbose) {
7480 out1str(": not found\n");
7481 }
7482 return 127;
7483 }
7484 out:
7485 outstr("\n", stdout);
7486 return 0;
7487}
7488
7489static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007490typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007491{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007492 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007493 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007494 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007495
Denis Vlasenko46846e22007-05-20 13:08:31 +00007496 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007497 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007498 i++;
7499 verbose = 0;
7500 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007501 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007502 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007503 }
7504 return err;
7505}
7506
7507#if ENABLE_ASH_CMDCMD
7508static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007509commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007510{
7511 int c;
7512 enum {
7513 VERIFY_BRIEF = 1,
7514 VERIFY_VERBOSE = 2,
7515 } verify = 0;
7516
7517 while ((c = nextopt("pvV")) != '\0')
7518 if (c == 'V')
7519 verify |= VERIFY_VERBOSE;
7520 else if (c == 'v')
7521 verify |= VERIFY_BRIEF;
7522#if DEBUG
7523 else if (c != 'p')
7524 abort();
7525#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007526 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7527 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007528 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007529 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007530
7531 return 0;
7532}
7533#endif
7534
7535
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007536/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007537
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007538static int funcblocksize; /* size of structures in function */
7539static int funcstringsize; /* size of strings in node */
7540static void *funcblock; /* block to allocate function from */
7541static char *funcstring; /* block to allocate strings from */
7542
Eric Andersencb57d552001-06-28 07:25:16 +00007543/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007544#define EV_EXIT 01 /* exit after evaluating tree */
7545#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7546#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007547
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007548static const short nodesize[26] = {
7549 SHELL_ALIGN(sizeof(struct ncmd)),
7550 SHELL_ALIGN(sizeof(struct npipe)),
7551 SHELL_ALIGN(sizeof(struct nredir)),
7552 SHELL_ALIGN(sizeof(struct nredir)),
7553 SHELL_ALIGN(sizeof(struct nredir)),
7554 SHELL_ALIGN(sizeof(struct nbinary)),
7555 SHELL_ALIGN(sizeof(struct nbinary)),
7556 SHELL_ALIGN(sizeof(struct nbinary)),
7557 SHELL_ALIGN(sizeof(struct nif)),
7558 SHELL_ALIGN(sizeof(struct nbinary)),
7559 SHELL_ALIGN(sizeof(struct nbinary)),
7560 SHELL_ALIGN(sizeof(struct nfor)),
7561 SHELL_ALIGN(sizeof(struct ncase)),
7562 SHELL_ALIGN(sizeof(struct nclist)),
7563 SHELL_ALIGN(sizeof(struct narg)),
7564 SHELL_ALIGN(sizeof(struct narg)),
7565 SHELL_ALIGN(sizeof(struct nfile)),
7566 SHELL_ALIGN(sizeof(struct nfile)),
7567 SHELL_ALIGN(sizeof(struct nfile)),
7568 SHELL_ALIGN(sizeof(struct nfile)),
7569 SHELL_ALIGN(sizeof(struct nfile)),
7570 SHELL_ALIGN(sizeof(struct ndup)),
7571 SHELL_ALIGN(sizeof(struct ndup)),
7572 SHELL_ALIGN(sizeof(struct nhere)),
7573 SHELL_ALIGN(sizeof(struct nhere)),
7574 SHELL_ALIGN(sizeof(struct nnot)),
7575};
7576
7577static void calcsize(union node *n);
7578
7579static void
7580sizenodelist(struct nodelist *lp)
7581{
7582 while (lp) {
7583 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7584 calcsize(lp->n);
7585 lp = lp->next;
7586 }
7587}
7588
7589static void
7590calcsize(union node *n)
7591{
7592 if (n == NULL)
7593 return;
7594 funcblocksize += nodesize[n->type];
7595 switch (n->type) {
7596 case NCMD:
7597 calcsize(n->ncmd.redirect);
7598 calcsize(n->ncmd.args);
7599 calcsize(n->ncmd.assign);
7600 break;
7601 case NPIPE:
7602 sizenodelist(n->npipe.cmdlist);
7603 break;
7604 case NREDIR:
7605 case NBACKGND:
7606 case NSUBSHELL:
7607 calcsize(n->nredir.redirect);
7608 calcsize(n->nredir.n);
7609 break;
7610 case NAND:
7611 case NOR:
7612 case NSEMI:
7613 case NWHILE:
7614 case NUNTIL:
7615 calcsize(n->nbinary.ch2);
7616 calcsize(n->nbinary.ch1);
7617 break;
7618 case NIF:
7619 calcsize(n->nif.elsepart);
7620 calcsize(n->nif.ifpart);
7621 calcsize(n->nif.test);
7622 break;
7623 case NFOR:
7624 funcstringsize += strlen(n->nfor.var) + 1;
7625 calcsize(n->nfor.body);
7626 calcsize(n->nfor.args);
7627 break;
7628 case NCASE:
7629 calcsize(n->ncase.cases);
7630 calcsize(n->ncase.expr);
7631 break;
7632 case NCLIST:
7633 calcsize(n->nclist.body);
7634 calcsize(n->nclist.pattern);
7635 calcsize(n->nclist.next);
7636 break;
7637 case NDEFUN:
7638 case NARG:
7639 sizenodelist(n->narg.backquote);
7640 funcstringsize += strlen(n->narg.text) + 1;
7641 calcsize(n->narg.next);
7642 break;
7643 case NTO:
7644 case NCLOBBER:
7645 case NFROM:
7646 case NFROMTO:
7647 case NAPPEND:
7648 calcsize(n->nfile.fname);
7649 calcsize(n->nfile.next);
7650 break;
7651 case NTOFD:
7652 case NFROMFD:
7653 calcsize(n->ndup.vname);
7654 calcsize(n->ndup.next);
7655 break;
7656 case NHERE:
7657 case NXHERE:
7658 calcsize(n->nhere.doc);
7659 calcsize(n->nhere.next);
7660 break;
7661 case NNOT:
7662 calcsize(n->nnot.com);
7663 break;
7664 };
7665}
7666
7667static char *
7668nodeckstrdup(char *s)
7669{
7670 char *rtn = funcstring;
7671
7672 strcpy(funcstring, s);
7673 funcstring += strlen(s) + 1;
7674 return rtn;
7675}
7676
7677static union node *copynode(union node *);
7678
7679static struct nodelist *
7680copynodelist(struct nodelist *lp)
7681{
7682 struct nodelist *start;
7683 struct nodelist **lpp;
7684
7685 lpp = &start;
7686 while (lp) {
7687 *lpp = funcblock;
7688 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7689 (*lpp)->n = copynode(lp->n);
7690 lp = lp->next;
7691 lpp = &(*lpp)->next;
7692 }
7693 *lpp = NULL;
7694 return start;
7695}
7696
7697static union node *
7698copynode(union node *n)
7699{
7700 union node *new;
7701
7702 if (n == NULL)
7703 return NULL;
7704 new = funcblock;
7705 funcblock = (char *) funcblock + nodesize[n->type];
7706
7707 switch (n->type) {
7708 case NCMD:
7709 new->ncmd.redirect = copynode(n->ncmd.redirect);
7710 new->ncmd.args = copynode(n->ncmd.args);
7711 new->ncmd.assign = copynode(n->ncmd.assign);
7712 break;
7713 case NPIPE:
7714 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007715 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007716 break;
7717 case NREDIR:
7718 case NBACKGND:
7719 case NSUBSHELL:
7720 new->nredir.redirect = copynode(n->nredir.redirect);
7721 new->nredir.n = copynode(n->nredir.n);
7722 break;
7723 case NAND:
7724 case NOR:
7725 case NSEMI:
7726 case NWHILE:
7727 case NUNTIL:
7728 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7729 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7730 break;
7731 case NIF:
7732 new->nif.elsepart = copynode(n->nif.elsepart);
7733 new->nif.ifpart = copynode(n->nif.ifpart);
7734 new->nif.test = copynode(n->nif.test);
7735 break;
7736 case NFOR:
7737 new->nfor.var = nodeckstrdup(n->nfor.var);
7738 new->nfor.body = copynode(n->nfor.body);
7739 new->nfor.args = copynode(n->nfor.args);
7740 break;
7741 case NCASE:
7742 new->ncase.cases = copynode(n->ncase.cases);
7743 new->ncase.expr = copynode(n->ncase.expr);
7744 break;
7745 case NCLIST:
7746 new->nclist.body = copynode(n->nclist.body);
7747 new->nclist.pattern = copynode(n->nclist.pattern);
7748 new->nclist.next = copynode(n->nclist.next);
7749 break;
7750 case NDEFUN:
7751 case NARG:
7752 new->narg.backquote = copynodelist(n->narg.backquote);
7753 new->narg.text = nodeckstrdup(n->narg.text);
7754 new->narg.next = copynode(n->narg.next);
7755 break;
7756 case NTO:
7757 case NCLOBBER:
7758 case NFROM:
7759 case NFROMTO:
7760 case NAPPEND:
7761 new->nfile.fname = copynode(n->nfile.fname);
7762 new->nfile.fd = n->nfile.fd;
7763 new->nfile.next = copynode(n->nfile.next);
7764 break;
7765 case NTOFD:
7766 case NFROMFD:
7767 new->ndup.vname = copynode(n->ndup.vname);
7768 new->ndup.dupfd = n->ndup.dupfd;
7769 new->ndup.fd = n->ndup.fd;
7770 new->ndup.next = copynode(n->ndup.next);
7771 break;
7772 case NHERE:
7773 case NXHERE:
7774 new->nhere.doc = copynode(n->nhere.doc);
7775 new->nhere.fd = n->nhere.fd;
7776 new->nhere.next = copynode(n->nhere.next);
7777 break;
7778 case NNOT:
7779 new->nnot.com = copynode(n->nnot.com);
7780 break;
7781 };
7782 new->type = n->type;
7783 return new;
7784}
7785
7786/*
7787 * Make a copy of a parse tree.
7788 */
7789static struct funcnode *
7790copyfunc(union node *n)
7791{
7792 struct funcnode *f;
7793 size_t blocksize;
7794
7795 funcblocksize = offsetof(struct funcnode, n);
7796 funcstringsize = 0;
7797 calcsize(n);
7798 blocksize = funcblocksize;
7799 f = ckmalloc(blocksize + funcstringsize);
7800 funcblock = (char *) f + offsetof(struct funcnode, n);
7801 funcstring = (char *) f + blocksize;
7802 copynode(n);
7803 f->count = 0;
7804 return f;
7805}
7806
7807/*
7808 * Define a shell function.
7809 */
7810static void
7811defun(char *name, union node *func)
7812{
7813 struct cmdentry entry;
7814
7815 INT_OFF;
7816 entry.cmdtype = CMDFUNCTION;
7817 entry.u.func = copyfunc(func);
7818 addcmdentry(name, &entry);
7819 INT_ON;
7820}
7821
7822static int evalskip; /* set if we are skipping commands */
7823/* reasons for skipping commands (see comment on breakcmd routine) */
7824#define SKIPBREAK (1 << 0)
7825#define SKIPCONT (1 << 1)
7826#define SKIPFUNC (1 << 2)
7827#define SKIPFILE (1 << 3)
7828#define SKIPEVAL (1 << 4)
7829static int skipcount; /* number of levels to skip */
7830static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007831static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007832
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007833/* forward decl way out to parsing code - dotrap needs it */
7834static int evalstring(char *s, int mask);
7835
7836/*
7837 * Called to execute a trap. Perhaps we should avoid entering new trap
7838 * handlers while we are executing a trap handler.
7839 */
7840static int
7841dotrap(void)
7842{
7843 char *p;
7844 char *q;
7845 int i;
7846 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007847 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007848
7849 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007850 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007851 xbarrier();
7852
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007853 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007854 if (!*q)
7855 continue;
7856 *q = '\0';
7857
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007858 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007859 if (!p)
7860 continue;
7861 skip = evalstring(p, SKIPEVAL);
7862 exitstatus = savestatus;
7863 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007864 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007865 }
7866
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007867 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007868}
7869
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007870/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007871static void evalloop(union node *, int);
7872static void evalfor(union node *, int);
7873static void evalcase(union node *, int);
7874static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007875static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007876static void evalpipe(union node *, int);
7877static void evalcommand(union node *, int);
7878static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007879static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007880
Eric Andersen62483552001-07-10 06:09:16 +00007881/*
Eric Andersenc470f442003-07-28 09:56:35 +00007882 * Evaluate a parse tree. The value is left in the global variable
7883 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007884 */
Eric Andersenc470f442003-07-28 09:56:35 +00007885static void
7886evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007887{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007888
7889 struct jmploc *volatile savehandler = exception_handler;
7890 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007891 int checkexit = 0;
7892 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007893 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007894
Eric Andersenc470f442003-07-28 09:56:35 +00007895 if (n == NULL) {
7896 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007897 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007898 }
Eric Andersenc470f442003-07-28 09:56:35 +00007899 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007900 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007901
7902 exception_handler = &jmploc;
7903 {
7904 int err = setjmp(jmploc.loc);
7905 if (err) {
7906 /* if it was a signal, check for trap handlers */
7907 if (exception == EXSIG)
7908 goto out;
7909 /* continue on the way out */
7910 exception_handler = savehandler;
7911 longjmp(exception_handler->loc, err);
7912 }
7913 }
7914
Eric Andersenc470f442003-07-28 09:56:35 +00007915 switch (n->type) {
7916 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007917#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007918 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007919 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007920 break;
7921#endif
7922 case NNOT:
7923 evaltree(n->nnot.com, EV_TESTED);
7924 status = !exitstatus;
7925 goto setstatus;
7926 case NREDIR:
7927 expredir(n->nredir.redirect);
7928 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7929 if (!status) {
7930 evaltree(n->nredir.n, flags & EV_TESTED);
7931 status = exitstatus;
7932 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007933 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00007934 goto setstatus;
7935 case NCMD:
7936 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007937 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007938 if (eflag && !(flags & EV_TESTED))
7939 checkexit = ~0;
7940 goto calleval;
7941 case NFOR:
7942 evalfn = evalfor;
7943 goto calleval;
7944 case NWHILE:
7945 case NUNTIL:
7946 evalfn = evalloop;
7947 goto calleval;
7948 case NSUBSHELL:
7949 case NBACKGND:
7950 evalfn = evalsubshell;
7951 goto calleval;
7952 case NPIPE:
7953 evalfn = evalpipe;
7954 goto checkexit;
7955 case NCASE:
7956 evalfn = evalcase;
7957 goto calleval;
7958 case NAND:
7959 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007960 case NSEMI: {
7961
Eric Andersenc470f442003-07-28 09:56:35 +00007962#if NAND + 1 != NOR
7963#error NAND + 1 != NOR
7964#endif
7965#if NOR + 1 != NSEMI
7966#error NOR + 1 != NSEMI
7967#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00007968 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00007969 evaltree(
7970 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007971 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00007972 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007973 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00007974 break;
7975 if (!evalskip) {
7976 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007977 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007978 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007979 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007980 evalfn(n, flags);
7981 break;
7982 }
7983 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007984 }
Eric Andersenc470f442003-07-28 09:56:35 +00007985 case NIF:
7986 evaltree(n->nif.test, EV_TESTED);
7987 if (evalskip)
7988 break;
7989 if (exitstatus == 0) {
7990 n = n->nif.ifpart;
7991 goto evaln;
7992 } else if (n->nif.elsepart) {
7993 n = n->nif.elsepart;
7994 goto evaln;
7995 }
7996 goto success;
7997 case NDEFUN:
7998 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007999 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008000 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008001 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008002 exitstatus = status;
8003 break;
8004 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008005
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008006 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008007 exception_handler = savehandler;
8008 out1:
8009 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008010 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008011 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008012 goto exexit;
8013
8014 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008015 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008016 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008017 }
Eric Andersen62483552001-07-10 06:09:16 +00008018}
8019
Eric Andersenc470f442003-07-28 09:56:35 +00008020#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8021static
8022#endif
8023void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8024
Eric Andersenc470f442003-07-28 09:56:35 +00008025static void
8026evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008027{
8028 int status;
8029
8030 loopnest++;
8031 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008032 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008033 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008034 int i;
8035
Eric Andersencb57d552001-06-28 07:25:16 +00008036 evaltree(n->nbinary.ch1, EV_TESTED);
8037 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008038 skipping:
8039 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008040 evalskip = 0;
8041 continue;
8042 }
8043 if (evalskip == SKIPBREAK && --skipcount <= 0)
8044 evalskip = 0;
8045 break;
8046 }
Eric Andersenc470f442003-07-28 09:56:35 +00008047 i = exitstatus;
8048 if (n->type != NWHILE)
8049 i = !i;
8050 if (i != 0)
8051 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008052 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008053 status = exitstatus;
8054 if (evalskip)
8055 goto skipping;
8056 }
8057 loopnest--;
8058 exitstatus = status;
8059}
8060
Eric Andersenc470f442003-07-28 09:56:35 +00008061static void
8062evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008063{
8064 struct arglist arglist;
8065 union node *argp;
8066 struct strlist *sp;
8067 struct stackmark smark;
8068
8069 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008070 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008071 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008072 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008073 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008074 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008075 if (evalskip)
8076 goto out;
8077 }
8078 *arglist.lastp = NULL;
8079
8080 exitstatus = 0;
8081 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008082 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008083 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008084 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008085 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008086 if (evalskip) {
8087 if (evalskip == SKIPCONT && --skipcount <= 0) {
8088 evalskip = 0;
8089 continue;
8090 }
8091 if (evalskip == SKIPBREAK && --skipcount <= 0)
8092 evalskip = 0;
8093 break;
8094 }
8095 }
8096 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008097 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008098 popstackmark(&smark);
8099}
8100
Eric Andersenc470f442003-07-28 09:56:35 +00008101static void
8102evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008103{
8104 union node *cp;
8105 union node *patp;
8106 struct arglist arglist;
8107 struct stackmark smark;
8108
8109 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008110 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008111 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008112 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008113 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008114 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8115 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008116 if (casematch(patp, arglist.list->text)) {
8117 if (evalskip == 0) {
8118 evaltree(cp->nclist.body, flags);
8119 }
8120 goto out;
8121 }
8122 }
8123 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008124 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008125 popstackmark(&smark);
8126}
8127
Eric Andersenc470f442003-07-28 09:56:35 +00008128/*
8129 * Kick off a subshell to evaluate a tree.
8130 */
Eric Andersenc470f442003-07-28 09:56:35 +00008131static void
8132evalsubshell(union node *n, int flags)
8133{
8134 struct job *jp;
8135 int backgnd = (n->type == NBACKGND);
8136 int status;
8137
8138 expredir(n->nredir.redirect);
8139 if (!backgnd && flags & EV_EXIT && !trap[0])
8140 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008141 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008142 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008143 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008144 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008145 flags |= EV_EXIT;
8146 if (backgnd)
8147 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008148 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008149 redirect(n->nredir.redirect, 0);
8150 evaltreenr(n->nredir.n, flags);
8151 /* never returns */
8152 }
8153 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008154 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008155 status = waitforjob(jp);
8156 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008157 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008158}
8159
Eric Andersenc470f442003-07-28 09:56:35 +00008160/*
8161 * Compute the names of the files in a redirection list.
8162 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008163static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008164static void
8165expredir(union node *n)
8166{
8167 union node *redir;
8168
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008169 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008170 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008171
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008172 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008173 fn.lastp = &fn.list;
8174 switch (redir->type) {
8175 case NFROMTO:
8176 case NFROM:
8177 case NTO:
8178 case NCLOBBER:
8179 case NAPPEND:
8180 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8181 redir->nfile.expfname = fn.list->text;
8182 break;
8183 case NFROMFD:
8184 case NTOFD:
8185 if (redir->ndup.vname) {
8186 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008187 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008188 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008189 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008190 }
8191 break;
8192 }
8193 }
8194}
8195
Eric Andersencb57d552001-06-28 07:25:16 +00008196/*
Eric Andersencb57d552001-06-28 07:25:16 +00008197 * Evaluate a pipeline. All the processes in the pipeline are children
8198 * of the process creating the pipeline. (This differs from some versions
8199 * of the shell, which make the last process in a pipeline the parent
8200 * of all the rest.)
8201 */
Eric Andersenc470f442003-07-28 09:56:35 +00008202static void
8203evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008204{
8205 struct job *jp;
8206 struct nodelist *lp;
8207 int pipelen;
8208 int prevfd;
8209 int pip[2];
8210
Eric Andersenc470f442003-07-28 09:56:35 +00008211 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008212 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008213 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008214 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008215 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008216 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008217 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008218 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008219 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008220 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008221 pip[1] = -1;
8222 if (lp->next) {
8223 if (pipe(pip) < 0) {
8224 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008225 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008226 }
8227 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008228 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008229 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008230 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008231 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008232 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008233 if (prevfd > 0) {
8234 dup2(prevfd, 0);
8235 close(prevfd);
8236 }
8237 if (pip[1] > 1) {
8238 dup2(pip[1], 1);
8239 close(pip[1]);
8240 }
Eric Andersenc470f442003-07-28 09:56:35 +00008241 evaltreenr(lp->n, flags);
8242 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008243 }
8244 if (prevfd >= 0)
8245 close(prevfd);
8246 prevfd = pip[0];
8247 close(pip[1]);
8248 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008249 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008250 exitstatus = waitforjob(jp);
8251 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008252 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008253 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008254}
8255
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008256/*
8257 * Controls whether the shell is interactive or not.
8258 */
8259static void
8260setinteractive(int on)
8261{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008262 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008263
8264 if (++on == is_interactive)
8265 return;
8266 is_interactive = on;
8267 setsignal(SIGINT);
8268 setsignal(SIGQUIT);
8269 setsignal(SIGTERM);
8270#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8271 if (is_interactive > 1) {
8272 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008273 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008274
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008275 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008276 out1fmt(
8277 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008278 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008279 "Enter 'help' for a list of built-in commands."
8280 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008281 bb_banner);
8282 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008283 }
8284 }
8285#endif
8286}
8287
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008288static void
8289optschanged(void)
8290{
8291#if DEBUG
8292 opentrace();
8293#endif
8294 setinteractive(iflag);
8295 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008296#if ENABLE_FEATURE_EDITING_VI
8297 if (viflag)
8298 line_input_state->flags |= VI_MODE;
8299 else
8300 line_input_state->flags &= ~VI_MODE;
8301#else
8302 viflag = 0; /* forcibly keep the option off */
8303#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008304}
8305
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008306static struct localvar *localvars;
8307
8308/*
8309 * Called after a function returns.
8310 * Interrupts must be off.
8311 */
8312static void
8313poplocalvars(void)
8314{
8315 struct localvar *lvp;
8316 struct var *vp;
8317
8318 while ((lvp = localvars) != NULL) {
8319 localvars = lvp->next;
8320 vp = lvp->vp;
8321 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8322 if (vp == NULL) { /* $- saved */
8323 memcpy(optlist, lvp->text, sizeof(optlist));
8324 free((char*)lvp->text);
8325 optschanged();
8326 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8327 unsetvar(vp->text);
8328 } else {
8329 if (vp->func)
8330 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8331 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8332 free((char*)vp->text);
8333 vp->flags = lvp->flags;
8334 vp->text = lvp->text;
8335 }
8336 free(lvp);
8337 }
8338}
8339
8340static int
8341evalfun(struct funcnode *func, int argc, char **argv, int flags)
8342{
8343 volatile struct shparam saveparam;
8344 struct localvar *volatile savelocalvars;
8345 struct jmploc *volatile savehandler;
8346 struct jmploc jmploc;
8347 int e;
8348
8349 saveparam = shellparam;
8350 savelocalvars = localvars;
8351 e = setjmp(jmploc.loc);
8352 if (e) {
8353 goto funcdone;
8354 }
8355 INT_OFF;
8356 savehandler = exception_handler;
8357 exception_handler = &jmploc;
8358 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008359 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008360 func->count++;
8361 funcnest++;
8362 INT_ON;
8363 shellparam.nparam = argc - 1;
8364 shellparam.p = argv + 1;
8365#if ENABLE_ASH_GETOPTS
8366 shellparam.optind = 1;
8367 shellparam.optoff = -1;
8368#endif
8369 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008370 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008371 INT_OFF;
8372 funcnest--;
8373 freefunc(func);
8374 poplocalvars();
8375 localvars = savelocalvars;
8376 freeparam(&shellparam);
8377 shellparam = saveparam;
8378 exception_handler = savehandler;
8379 INT_ON;
8380 evalskip &= ~SKIPFUNC;
8381 return e;
8382}
8383
Denis Vlasenko131ae172007-02-18 13:00:19 +00008384#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008385static char **
8386parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008387{
8388 char *cp, c;
8389
8390 for (;;) {
8391 cp = *++argv;
8392 if (!cp)
8393 return 0;
8394 if (*cp++ != '-')
8395 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008396 c = *cp++;
8397 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008398 break;
8399 if (c == '-' && !*cp) {
8400 argv++;
8401 break;
8402 }
8403 do {
8404 switch (c) {
8405 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008406 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008407 break;
8408 default:
8409 /* run 'typecmd' for other options */
8410 return 0;
8411 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008412 c = *cp++;
8413 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008414 }
8415 return argv;
8416}
8417#endif
8418
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008419/*
8420 * Make a variable a local variable. When a variable is made local, it's
8421 * value and flags are saved in a localvar structure. The saved values
8422 * will be restored when the shell function returns. We handle the name
8423 * "-" as a special case.
8424 */
8425static void
8426mklocal(char *name)
8427{
8428 struct localvar *lvp;
8429 struct var **vpp;
8430 struct var *vp;
8431
8432 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008433 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008434 if (LONE_DASH(name)) {
8435 char *p;
8436 p = ckmalloc(sizeof(optlist));
8437 lvp->text = memcpy(p, optlist, sizeof(optlist));
8438 vp = NULL;
8439 } else {
8440 char *eq;
8441
8442 vpp = hashvar(name);
8443 vp = *findvar(vpp, name);
8444 eq = strchr(name, '=');
8445 if (vp == NULL) {
8446 if (eq)
8447 setvareq(name, VSTRFIXED);
8448 else
8449 setvar(name, NULL, VSTRFIXED);
8450 vp = *vpp; /* the new variable */
8451 lvp->flags = VUNSET;
8452 } else {
8453 lvp->text = vp->text;
8454 lvp->flags = vp->flags;
8455 vp->flags |= VSTRFIXED|VTEXTFIXED;
8456 if (eq)
8457 setvareq(name, 0);
8458 }
8459 }
8460 lvp->vp = vp;
8461 lvp->next = localvars;
8462 localvars = lvp;
8463 INT_ON;
8464}
8465
8466/*
8467 * The "local" command.
8468 */
8469static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008470localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008471{
8472 char *name;
8473
8474 argv = argptr;
8475 while ((name = *argv++) != NULL) {
8476 mklocal(name);
8477 }
8478 return 0;
8479}
8480
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008481static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008482falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008483{
8484 return 1;
8485}
8486
8487static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008488truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008489{
8490 return 0;
8491}
8492
8493static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008494execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008495{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008496 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008497 iflag = 0; /* exit on error */
8498 mflag = 0;
8499 optschanged();
8500 shellexec(argv + 1, pathval(), 0);
8501 }
8502 return 0;
8503}
8504
8505/*
8506 * The return command.
8507 */
8508static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008509returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008510{
8511 /*
8512 * If called outside a function, do what ksh does;
8513 * skip the rest of the file.
8514 */
8515 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8516 return argv[1] ? number(argv[1]) : exitstatus;
8517}
8518
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008519/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008520static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008521static int dotcmd(int, char **);
8522static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008523static int exitcmd(int, char **);
8524static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008525#if ENABLE_ASH_GETOPTS
8526static int getoptscmd(int, char **);
8527#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008528#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008529static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008530#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008531#if ENABLE_ASH_MATH_SUPPORT
8532static int letcmd(int, char **);
8533#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008534static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008535static int setcmd(int, char **);
8536static int shiftcmd(int, char **);
8537static int timescmd(int, char **);
8538static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008539static int umaskcmd(int, char **);
8540static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008541static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008542
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008543#define BUILTIN_NOSPEC "0"
8544#define BUILTIN_SPECIAL "1"
8545#define BUILTIN_REGULAR "2"
8546#define BUILTIN_SPEC_REG "3"
8547#define BUILTIN_ASSIGN "4"
8548#define BUILTIN_SPEC_ASSG "5"
8549#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008550#define BUILTIN_SPEC_REG_ASSG "7"
8551
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008552/* We do not handle [[ expr ]] bashism bash-compatibly,
8553 * we make it a synonym of [ expr ].
8554 * Basically, word splitting and pathname expansion should NOT be performed
8555 * Examples:
8556 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8557 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8558 * Additional operators:
8559 * || and && should work as -o and -a
8560 * =~ regexp match
8561 * Apart from the above, [[ expr ]] should work as [ expr ]
8562 */
8563
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008564#define echocmd echo_main
8565#define printfcmd printf_main
8566#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008567
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008568/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008569static const struct builtincmd builtintab[] = {
8570 { BUILTIN_SPEC_REG ".", dotcmd },
8571 { BUILTIN_SPEC_REG ":", truecmd },
8572#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008573 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008574#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008575 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008576#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008577#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008578#if ENABLE_ASH_ALIAS
8579 { BUILTIN_REG_ASSG "alias", aliascmd },
8580#endif
8581#if JOBS
8582 { BUILTIN_REGULAR "bg", fg_bgcmd },
8583#endif
8584 { BUILTIN_SPEC_REG "break", breakcmd },
8585 { BUILTIN_REGULAR "cd", cdcmd },
8586 { BUILTIN_NOSPEC "chdir", cdcmd },
8587#if ENABLE_ASH_CMDCMD
8588 { BUILTIN_REGULAR "command", commandcmd },
8589#endif
8590 { BUILTIN_SPEC_REG "continue", breakcmd },
8591#if ENABLE_ASH_BUILTIN_ECHO
8592 { BUILTIN_REGULAR "echo", echocmd },
8593#endif
8594 { BUILTIN_SPEC_REG "eval", evalcmd },
8595 { BUILTIN_SPEC_REG "exec", execcmd },
8596 { BUILTIN_SPEC_REG "exit", exitcmd },
8597 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8598 { BUILTIN_REGULAR "false", falsecmd },
8599#if JOBS
8600 { BUILTIN_REGULAR "fg", fg_bgcmd },
8601#endif
8602#if ENABLE_ASH_GETOPTS
8603 { BUILTIN_REGULAR "getopts", getoptscmd },
8604#endif
8605 { BUILTIN_NOSPEC "hash", hashcmd },
8606#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8607 { BUILTIN_NOSPEC "help", helpcmd },
8608#endif
8609#if JOBS
8610 { BUILTIN_REGULAR "jobs", jobscmd },
8611 { BUILTIN_REGULAR "kill", killcmd },
8612#endif
8613#if ENABLE_ASH_MATH_SUPPORT
8614 { BUILTIN_NOSPEC "let", letcmd },
8615#endif
8616 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008617#if ENABLE_ASH_BUILTIN_PRINTF
8618 { BUILTIN_REGULAR "printf", printfcmd },
8619#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008620 { BUILTIN_NOSPEC "pwd", pwdcmd },
8621 { BUILTIN_REGULAR "read", readcmd },
8622 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8623 { BUILTIN_SPEC_REG "return", returncmd },
8624 { BUILTIN_SPEC_REG "set", setcmd },
8625 { BUILTIN_SPEC_REG "shift", shiftcmd },
8626 { BUILTIN_SPEC_REG "source", dotcmd },
8627#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008628 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008629#endif
8630 { BUILTIN_SPEC_REG "times", timescmd },
8631 { BUILTIN_SPEC_REG "trap", trapcmd },
8632 { BUILTIN_REGULAR "true", truecmd },
8633 { BUILTIN_NOSPEC "type", typecmd },
8634 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8635 { BUILTIN_REGULAR "umask", umaskcmd },
8636#if ENABLE_ASH_ALIAS
8637 { BUILTIN_REGULAR "unalias", unaliascmd },
8638#endif
8639 { BUILTIN_SPEC_REG "unset", unsetcmd },
8640 { BUILTIN_REGULAR "wait", waitcmd },
8641};
8642
Denis Vlasenko80591b02008-03-25 07:49:43 +00008643/* Should match the above table! */
8644#define COMMANDCMD (builtintab + \
8645 2 + \
8646 1 * ENABLE_ASH_BUILTIN_TEST + \
8647 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8648 1 * ENABLE_ASH_ALIAS + \
8649 1 * ENABLE_ASH_JOB_CONTROL + \
8650 3)
8651#define EXECCMD (builtintab + \
8652 2 + \
8653 1 * ENABLE_ASH_BUILTIN_TEST + \
8654 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8655 1 * ENABLE_ASH_ALIAS + \
8656 1 * ENABLE_ASH_JOB_CONTROL + \
8657 3 + \
8658 1 * ENABLE_ASH_CMDCMD + \
8659 1 + \
8660 ENABLE_ASH_BUILTIN_ECHO + \
8661 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008662
8663/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008664 * Search the table of builtin commands.
8665 */
8666static struct builtincmd *
8667find_builtin(const char *name)
8668{
8669 struct builtincmd *bp;
8670
8671 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008672 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008673 pstrcmp
8674 );
8675 return bp;
8676}
8677
8678/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008679 * Execute a simple command.
8680 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008681static int
8682isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008683{
8684 const char *q = endofname(p);
8685 if (p == q)
8686 return 0;
8687 return *q == '=';
8688}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008689static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008690bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008691{
8692 /* Preserve exitstatus of a previous possible redirection
8693 * as POSIX mandates */
8694 return back_exitstatus;
8695}
Eric Andersenc470f442003-07-28 09:56:35 +00008696static void
8697evalcommand(union node *cmd, int flags)
8698{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008699 static const struct builtincmd null_bltin = {
8700 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008701 };
Eric Andersenc470f442003-07-28 09:56:35 +00008702 struct stackmark smark;
8703 union node *argp;
8704 struct arglist arglist;
8705 struct arglist varlist;
8706 char **argv;
8707 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008708 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008709 struct cmdentry cmdentry;
8710 struct job *jp;
8711 char *lastarg;
8712 const char *path;
8713 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008714 int status;
8715 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008716 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008717 smallint cmd_is_exec;
8718 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008719
8720 /* First expand the arguments. */
8721 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8722 setstackmark(&smark);
8723 back_exitstatus = 0;
8724
8725 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008726 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008727 varlist.lastp = &varlist.list;
8728 *varlist.lastp = NULL;
8729 arglist.lastp = &arglist.list;
8730 *arglist.lastp = NULL;
8731
8732 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008733 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008734 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8735 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8736 }
8737
Eric Andersenc470f442003-07-28 09:56:35 +00008738 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8739 struct strlist **spp;
8740
8741 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008742 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008743 expandarg(argp, &arglist, EXP_VARTILDE);
8744 else
8745 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8746
Eric Andersenc470f442003-07-28 09:56:35 +00008747 for (sp = *spp; sp; sp = sp->next)
8748 argc++;
8749 }
8750
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008751 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008752 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008753 TRACE(("evalcommand arg: %s\n", sp->text));
8754 *nargv++ = sp->text;
8755 }
8756 *nargv = NULL;
8757
8758 lastarg = NULL;
8759 if (iflag && funcnest == 0 && argc > 0)
8760 lastarg = nargv[-1];
8761
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008762 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008763 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008764 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008765
8766 path = vpath.text;
8767 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8768 struct strlist **spp;
8769 char *p;
8770
8771 spp = varlist.lastp;
8772 expandarg(argp, &varlist, EXP_VARTILDE);
8773
8774 /*
8775 * Modify the command lookup path, if a PATH= assignment
8776 * is present
8777 */
8778 p = (*spp)->text;
8779 if (varequal(p, path))
8780 path = p;
8781 }
8782
8783 /* Print the command if xflag is set. */
8784 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008785 int n;
8786 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008787
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008788 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008789 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008790
8791 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008792 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008793 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008794 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008795 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008796 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008797 p--;
8798 }
8799 }
8800 sp = arglist.list;
8801 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008802 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008803 }
8804
8805 cmd_is_exec = 0;
8806 spclbltin = -1;
8807
8808 /* Now locate the command. */
8809 if (argc) {
8810 const char *oldpath;
8811 int cmd_flag = DO_ERR;
8812
8813 path += 5;
8814 oldpath = path;
8815 for (;;) {
8816 find_command(argv[0], &cmdentry, cmd_flag, path);
8817 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008818 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008819 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008820 goto bail;
8821 }
8822
8823 /* implement bltin and command here */
8824 if (cmdentry.cmdtype != CMDBUILTIN)
8825 break;
8826 if (spclbltin < 0)
8827 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8828 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008829 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008830#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008831 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008832 path = oldpath;
8833 nargv = parse_command_args(argv, &path);
8834 if (!nargv)
8835 break;
8836 argc -= nargv - argv;
8837 argv = nargv;
8838 cmd_flag |= DO_NOFUNC;
8839 } else
8840#endif
8841 break;
8842 }
8843 }
8844
8845 if (status) {
8846 /* We have a redirection error. */
8847 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008848 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008849 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008850 exitstatus = status;
8851 goto out;
8852 }
8853
8854 /* Execute the command. */
8855 switch (cmdentry.cmdtype) {
8856 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008857#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008858 {
8859 /* find_command() encodes applet_no as (-2 - applet_no) */
8860 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008861 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008862 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008863 /* run <applet>_main() */
8864 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008865 break;
8866 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008867 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008868#endif
8869
Eric Andersenc470f442003-07-28 09:56:35 +00008870 /* Fork off a child process if necessary. */
8871 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008872 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008873 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008874 if (forkshell(jp, cmd, FORK_FG) != 0) {
8875 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008876 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008877 break;
8878 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008879 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008880 }
8881 listsetvar(varlist.list, VEXPORT|VSTACK);
8882 shellexec(argv, path, cmdentry.u.index);
8883 /* NOTREACHED */
8884
8885 case CMDBUILTIN:
8886 cmdenviron = varlist.list;
8887 if (cmdenviron) {
8888 struct strlist *list = cmdenviron;
8889 int i = VNOSET;
8890 if (spclbltin > 0 || argc == 0) {
8891 i = 0;
8892 if (cmd_is_exec && argc > 1)
8893 i = VEXPORT;
8894 }
8895 listsetvar(list, i);
8896 }
8897 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8898 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008899 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008900 if (i == EXEXIT)
8901 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008902 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008903 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008904 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008905 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008906 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008907 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008908 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008909 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008910 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008911 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008912 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008913 }
8914 break;
8915
8916 case CMDFUNCTION:
8917 listsetvar(varlist.list, 0);
8918 if (evalfun(cmdentry.u.func, argc, argv, flags))
8919 goto raise;
8920 break;
8921 }
8922
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008923 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008924 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008925 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008926 /* dsl: I think this is intended to be used to support
8927 * '_' in 'vi' command mode during line editing...
8928 * However I implemented that within libedit itself.
8929 */
8930 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008931 }
Eric Andersenc470f442003-07-28 09:56:35 +00008932 popstackmark(&smark);
8933}
8934
8935static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008936evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8937{
Eric Andersenc470f442003-07-28 09:56:35 +00008938 char *volatile savecmdname;
8939 struct jmploc *volatile savehandler;
8940 struct jmploc jmploc;
8941 int i;
8942
8943 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008944 i = setjmp(jmploc.loc);
8945 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008946 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008947 savehandler = exception_handler;
8948 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008949 commandname = argv[0];
8950 argptr = argv + 1;
8951 optptr = NULL; /* initialize nextopt */
8952 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008953 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008954 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008955 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008956 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008957 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008958// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008959 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008960
8961 return i;
8962}
8963
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008964static int
8965goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008966{
8967 return !*endofname(p);
8968}
8969
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008970
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008971/*
8972 * Search for a command. This is called before we fork so that the
8973 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008974 * the child. The check for "goodname" is an overly conservative
8975 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008976 */
Eric Andersenc470f442003-07-28 09:56:35 +00008977static void
8978prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008979{
8980 struct cmdentry entry;
8981
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008982 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8983 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008984}
8985
Eric Andersencb57d552001-06-28 07:25:16 +00008986
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008987/* ============ Builtin commands
8988 *
8989 * Builtin commands whose functions are closely tied to evaluation
8990 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008991 */
8992
8993/*
Eric Andersencb57d552001-06-28 07:25:16 +00008994 * Handle break and continue commands. Break, continue, and return are
8995 * all handled by setting the evalskip flag. The evaluation routines
8996 * above all check this flag, and if it is set they start skipping
8997 * commands rather than executing them. The variable skipcount is
8998 * the number of loops to break/continue, or the number of function
8999 * levels to return. (The latter is always 1.) It should probably
9000 * be an error to break out of more loops than exist, but it isn't
9001 * in the standard shell so we don't make it one here.
9002 */
Eric Andersenc470f442003-07-28 09:56:35 +00009003static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009004breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009005{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009006 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009007
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009008 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009009 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009010 if (n > loopnest)
9011 n = loopnest;
9012 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009013 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009014 skipcount = n;
9015 }
9016 return 0;
9017}
9018
Eric Andersenc470f442003-07-28 09:56:35 +00009019
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009020/* ============ input.c
9021 *
Eric Andersen90898442003-08-06 11:20:52 +00009022 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009023 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009024
Eric Andersenc470f442003-07-28 09:56:35 +00009025#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009026
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009027enum {
9028 INPUT_PUSH_FILE = 1,
9029 INPUT_NOFILE_OK = 2,
9030};
Eric Andersencb57d552001-06-28 07:25:16 +00009031
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009032static int plinno = 1; /* input line number */
9033/* number of characters left in input buffer */
9034static int parsenleft; /* copy of parsefile->nleft */
9035static int parselleft; /* copy of parsefile->lleft */
9036/* next character in input buffer */
9037static char *parsenextc; /* copy of parsefile->nextc */
9038
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009039static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009040/* values of checkkwd variable */
9041#define CHKALIAS 0x1
9042#define CHKKWD 0x2
9043#define CHKNL 0x4
9044
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009045static void
9046popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009047{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009048 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009049
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009050 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009051#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009052 if (sp->ap) {
9053 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9054 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009055 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009056 if (sp->string != sp->ap->val) {
9057 free(sp->string);
9058 }
9059 sp->ap->flag &= ~ALIASINUSE;
9060 if (sp->ap->flag & ALIASDEAD) {
9061 unalias(sp->ap->name);
9062 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009063 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009064#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009065 parsenextc = sp->prevstring;
9066 parsenleft = sp->prevnleft;
9067/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009068 g_parsefile->strpush = sp->prev;
9069 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009070 free(sp);
9071 INT_ON;
9072}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009073
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009074static int
9075preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009076{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009077 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009078 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009079 parsenextc = buf;
9080
Denis Vlasenko38f63192007-01-22 09:03:07 +00009081#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009082 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009083 if (!iflag || g_parsefile->fd)
9084 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009085 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009086#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009087 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009088#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009089 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9090 if (nr == 0) {
9091 /* Ctrl+C pressed */
9092 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009093 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009094 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009095 raise(SIGINT);
9096 return 1;
9097 }
Eric Andersenc470f442003-07-28 09:56:35 +00009098 goto retry;
9099 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009100 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009101 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009102 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009103 }
Eric Andersencb57d552001-06-28 07:25:16 +00009104 }
9105#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009106 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009107#endif
9108
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009109#if 0
9110/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009111 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009112 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009113 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009114 if (flags >= 0 && (flags & O_NONBLOCK)) {
9115 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009116 if (fcntl(0, F_SETFL, flags) >= 0) {
9117 out2str("sh: turning off NDELAY mode\n");
9118 goto retry;
9119 }
9120 }
9121 }
9122 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009123#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009124 return nr;
9125}
9126
9127/*
9128 * Refill the input buffer and return the next input character:
9129 *
9130 * 1) If a string was pushed back on the input, pop it;
9131 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9132 * from a string so we can't refill the buffer, return EOF.
9133 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9134 * 4) Process input up to the next newline, deleting nul characters.
9135 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009136static int
Eric Andersenc470f442003-07-28 09:56:35 +00009137preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009138{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009139 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009140 int more;
9141 char savec;
9142
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009143 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009144#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009145 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009146 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009147 return PEOA;
9148 }
Eric Andersen2870d962001-07-02 17:27:21 +00009149#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009150 popstring();
9151 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009152 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009153 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009154 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009155 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009156 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009157
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009158 more = parselleft;
9159 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009160 again:
9161 more = preadfd();
9162 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009163 parselleft = parsenleft = EOF_NLEFT;
9164 return PEOF;
9165 }
9166 }
9167
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009168 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009169
9170 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009171 for (;;) {
9172 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009173
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009174 more--;
9175 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009176
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009177 if (!c)
9178 memmove(q, q + 1, more);
9179 else {
9180 q++;
9181 if (c == '\n') {
9182 parsenleft = q - parsenextc - 1;
9183 break;
9184 }
Eric Andersencb57d552001-06-28 07:25:16 +00009185 }
9186
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009187 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009188 parsenleft = q - parsenextc - 1;
9189 if (parsenleft < 0)
9190 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009191 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009192 }
9193 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009194 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009195
9196 savec = *q;
9197 *q = '\0';
9198
9199 if (vflag) {
9200 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009201 }
9202
9203 *q = savec;
9204
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009205 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009206}
9207
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009208#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009209static int
9210pgetc(void)
9211{
9212 return pgetc_as_macro();
9213}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009214
9215#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9216#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009217#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009218#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009219#endif
9220
9221/*
9222 * Same as pgetc(), but ignores PEOA.
9223 */
9224#if ENABLE_ASH_ALIAS
9225static int
9226pgetc2(void)
9227{
9228 int c;
9229
9230 do {
9231 c = pgetc_macro();
9232 } while (c == PEOA);
9233 return c;
9234}
9235#else
9236static int
9237pgetc2(void)
9238{
9239 return pgetc_macro();
9240}
9241#endif
9242
9243/*
9244 * Read a line from the script.
9245 */
9246static char *
9247pfgets(char *line, int len)
9248{
9249 char *p = line;
9250 int nleft = len;
9251 int c;
9252
9253 while (--nleft > 0) {
9254 c = pgetc2();
9255 if (c == PEOF) {
9256 if (p == line)
9257 return NULL;
9258 break;
9259 }
9260 *p++ = c;
9261 if (c == '\n')
9262 break;
9263 }
9264 *p = '\0';
9265 return line;
9266}
9267
Eric Andersenc470f442003-07-28 09:56:35 +00009268/*
9269 * Undo the last call to pgetc. Only one character may be pushed back.
9270 * PEOF may be pushed back.
9271 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009272static void
Eric Andersenc470f442003-07-28 09:56:35 +00009273pungetc(void)
9274{
9275 parsenleft++;
9276 parsenextc--;
9277}
Eric Andersencb57d552001-06-28 07:25:16 +00009278
9279/*
9280 * Push a string back onto the input at this current parsefile level.
9281 * We handle aliases this way.
9282 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009283#if !ENABLE_ASH_ALIAS
9284#define pushstring(s, ap) pushstring(s)
9285#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009286static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009287pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009288{
Eric Andersencb57d552001-06-28 07:25:16 +00009289 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009290 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009291
Eric Andersenc470f442003-07-28 09:56:35 +00009292 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009293 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009294/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009295 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009296 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009297 sp->prev = g_parsefile->strpush;
9298 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009299 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009300 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009301 sp->prevstring = parsenextc;
9302 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009303#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009304 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009305 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009306 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009307 sp->string = s;
9308 }
Eric Andersen2870d962001-07-02 17:27:21 +00009309#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009310 parsenextc = s;
9311 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009312 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009313}
9314
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009315/*
9316 * To handle the "." command, a stack of input files is used. Pushfile
9317 * adds a new entry to the stack and popfile restores the previous level.
9318 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009319static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009320pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009321{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009322 struct parsefile *pf;
9323
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009324 g_parsefile->nleft = parsenleft;
9325 g_parsefile->lleft = parselleft;
9326 g_parsefile->nextc = parsenextc;
9327 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009328 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009329 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009330 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009331 /*pf->strpush = NULL; - ckzalloc did it */
9332 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009333 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009334}
9335
9336static void
9337popfile(void)
9338{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009339 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009340
Denis Vlasenkob012b102007-02-19 22:43:01 +00009341 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009342 if (pf->fd >= 0)
9343 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009344 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009345 while (pf->strpush)
9346 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009347 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009348 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009349 parsenleft = g_parsefile->nleft;
9350 parselleft = g_parsefile->lleft;
9351 parsenextc = g_parsefile->nextc;
9352 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009353 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009354}
9355
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009356/*
9357 * Return to top level.
9358 */
9359static void
9360popallfiles(void)
9361{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009362 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009363 popfile();
9364}
9365
9366/*
9367 * Close the file(s) that the shell is reading commands from. Called
9368 * after a fork is done.
9369 */
9370static void
9371closescript(void)
9372{
9373 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009374 if (g_parsefile->fd > 0) {
9375 close(g_parsefile->fd);
9376 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009377 }
9378}
9379
9380/*
9381 * Like setinputfile, but takes an open file descriptor. Call this with
9382 * interrupts off.
9383 */
9384static void
9385setinputfd(int fd, int push)
9386{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009387 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009388 if (push) {
9389 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009390 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009391 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009392 g_parsefile->fd = fd;
9393 if (g_parsefile->buf == NULL)
9394 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009395 parselleft = parsenleft = 0;
9396 plinno = 1;
9397}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009398
Eric Andersenc470f442003-07-28 09:56:35 +00009399/*
9400 * Set the input to take input from a file. If push is set, push the
9401 * old input onto the stack first.
9402 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009403static int
9404setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009405{
9406 int fd;
9407 int fd2;
9408
Denis Vlasenkob012b102007-02-19 22:43:01 +00009409 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009410 fd = open(fname, O_RDONLY);
9411 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009412 if (flags & INPUT_NOFILE_OK)
9413 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009414 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009415 }
Eric Andersenc470f442003-07-28 09:56:35 +00009416 if (fd < 10) {
9417 fd2 = copyfd(fd, 10);
9418 close(fd);
9419 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009420 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009421 fd = fd2;
9422 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009423 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009424 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009425 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009426 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009427}
9428
Eric Andersencb57d552001-06-28 07:25:16 +00009429/*
9430 * Like setinputfile, but takes input from a string.
9431 */
Eric Andersenc470f442003-07-28 09:56:35 +00009432static void
9433setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009434{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009435 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009436 pushfile();
9437 parsenextc = string;
9438 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009439 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009440 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009441 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009442}
9443
9444
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009445/* ============ mail.c
9446 *
9447 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009448 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009449
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009450#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009451
Eric Andersencb57d552001-06-28 07:25:16 +00009452#define MAXMBOXES 10
9453
Eric Andersenc470f442003-07-28 09:56:35 +00009454/* times of mailboxes */
9455static time_t mailtime[MAXMBOXES];
9456/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009457static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009458
Eric Andersencb57d552001-06-28 07:25:16 +00009459/*
Eric Andersenc470f442003-07-28 09:56:35 +00009460 * Print appropriate message(s) if mail has arrived.
9461 * If mail_var_path_changed is set,
9462 * then the value of MAIL has mail_var_path_changed,
9463 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009464 */
Eric Andersenc470f442003-07-28 09:56:35 +00009465static void
9466chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009467{
Eric Andersencb57d552001-06-28 07:25:16 +00009468 const char *mpath;
9469 char *p;
9470 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009471 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009472 struct stackmark smark;
9473 struct stat statb;
9474
Eric Andersencb57d552001-06-28 07:25:16 +00009475 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009476 mpath = mpathset() ? mpathval() : mailval();
9477 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009478 p = padvance(&mpath, nullstr);
9479 if (p == NULL)
9480 break;
9481 if (*p == '\0')
9482 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009483 for (q = p; *q; q++)
9484 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009485#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009486 if (q[-1] != '/')
9487 abort();
9488#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009489 q[-1] = '\0'; /* delete trailing '/' */
9490 if (stat(p, &statb) < 0) {
9491 *mtp = 0;
9492 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009493 }
Eric Andersenc470f442003-07-28 09:56:35 +00009494 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9495 fprintf(
9496 stderr, snlfmt,
9497 pathopt ? pathopt : "you have mail"
9498 );
9499 }
9500 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009501 }
Eric Andersenc470f442003-07-28 09:56:35 +00009502 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009503 popstackmark(&smark);
9504}
Eric Andersencb57d552001-06-28 07:25:16 +00009505
Eric Andersenc470f442003-07-28 09:56:35 +00009506static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009507changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009508{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009509 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009510}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009511
Denis Vlasenko131ae172007-02-18 13:00:19 +00009512#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009513
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009514
9515/* ============ ??? */
9516
Eric Andersencb57d552001-06-28 07:25:16 +00009517/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009518 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009519 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009520static void
9521setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009522{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009523 char **newparam;
9524 char **ap;
9525 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009526
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009527 for (nparam = 0; argv[nparam]; nparam++)
9528 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009529 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9530 while (*argv) {
9531 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009532 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009533 *ap = NULL;
9534 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009535 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009536 shellparam.nparam = nparam;
9537 shellparam.p = newparam;
9538#if ENABLE_ASH_GETOPTS
9539 shellparam.optind = 1;
9540 shellparam.optoff = -1;
9541#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009542}
9543
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009544/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009545 * Process shell options. The global variable argptr contains a pointer
9546 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009547 *
9548 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9549 * For a non-interactive shell, an error condition encountered
9550 * by a special built-in ... shall cause the shell to write a diagnostic message
9551 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009552 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009553 * ...
9554 * Utility syntax error (option or operand error) Shall exit
9555 * ...
9556 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9557 * we see that bash does not do that (set "finishes" with error code 1 instead,
9558 * and shell continues), and people rely on this behavior!
9559 * Testcase:
9560 * set -o barfoo 2>/dev/null
9561 * echo $?
9562 *
9563 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009564 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009565static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009566plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009567{
9568 int i;
9569
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009570 if (name) {
9571 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009572 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009573 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009574 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009575 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009576 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009577 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009578 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009579 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009580 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009581 if (val) {
9582 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9583 } else {
9584 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9585 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009586 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009587 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009588}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009589static void
9590setoption(int flag, int val)
9591{
9592 int i;
9593
9594 for (i = 0; i < NOPTS; i++) {
9595 if (optletters(i) == flag) {
9596 optlist[i] = val;
9597 return;
9598 }
9599 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009600 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009601 /* NOTREACHED */
9602}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009603static int
Eric Andersenc470f442003-07-28 09:56:35 +00009604options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009605{
9606 char *p;
9607 int val;
9608 int c;
9609
9610 if (cmdline)
9611 minusc = NULL;
9612 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009613 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009614 if (c != '-' && c != '+')
9615 break;
9616 argptr++;
9617 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009618 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009619 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009620 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009621 if (!cmdline) {
9622 /* "-" means turn off -x and -v */
9623 if (p[0] == '\0')
9624 xflag = vflag = 0;
9625 /* "--" means reset params */
9626 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009627 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009628 }
Eric Andersenc470f442003-07-28 09:56:35 +00009629 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009630 }
Eric Andersencb57d552001-06-28 07:25:16 +00009631 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009632 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009633 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009634 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009635 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009636 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009637 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009638 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009639 /* it already printed err message */
9640 return 1; /* error */
9641 }
Eric Andersencb57d552001-06-28 07:25:16 +00009642 if (*argptr)
9643 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009644 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9645 isloginsh = 1;
9646 /* bash does not accept +-login, we also won't */
9647 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009648 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009649 isloginsh = 1;
9650 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009651 } else {
9652 setoption(c, val);
9653 }
9654 }
9655 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009656 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009657}
9658
Eric Andersencb57d552001-06-28 07:25:16 +00009659/*
Eric Andersencb57d552001-06-28 07:25:16 +00009660 * The shift builtin command.
9661 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009662static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009663shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009664{
9665 int n;
9666 char **ap1, **ap2;
9667
9668 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009669 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009670 n = number(argv[1]);
9671 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009672 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009673 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009674 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009675 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009676 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009677 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009678 }
9679 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009680 while ((*ap2++ = *ap1++) != NULL)
9681 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009682#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009683 shellparam.optind = 1;
9684 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009685#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009686 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009687 return 0;
9688}
9689
Eric Andersencb57d552001-06-28 07:25:16 +00009690/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009691 * POSIX requires that 'set' (but not export or readonly) output the
9692 * variables in lexicographic order - by the locale's collating order (sigh).
9693 * Maybe we could keep them in an ordered balanced binary tree
9694 * instead of hashed lists.
9695 * For now just roll 'em through qsort for printing...
9696 */
9697static int
9698showvars(const char *sep_prefix, int on, int off)
9699{
9700 const char *sep;
9701 char **ep, **epend;
9702
9703 ep = listvars(on, off, &epend);
9704 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9705
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009706 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009707
9708 for (; ep < epend; ep++) {
9709 const char *p;
9710 const char *q;
9711
9712 p = strchrnul(*ep, '=');
9713 q = nullstr;
9714 if (*p)
9715 q = single_quote(++p);
9716 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9717 }
9718 return 0;
9719}
9720
9721/*
Eric Andersencb57d552001-06-28 07:25:16 +00009722 * The set command builtin.
9723 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009724static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009725setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009726{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009727 int retval;
9728
Denis Vlasenko68404f12008-03-17 09:00:54 +00009729 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009730 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009731 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009732 retval = 1;
9733 if (!options(0)) { /* if no parse error... */
9734 retval = 0;
9735 optschanged();
9736 if (*argptr != NULL) {
9737 setparam(argptr);
9738 }
Eric Andersencb57d552001-06-28 07:25:16 +00009739 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009740 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009741 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009742}
9743
Denis Vlasenko131ae172007-02-18 13:00:19 +00009744#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009745static void
9746change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009747{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009748 /* Galois LFSR parameter */
9749 /* Taps at 32 31 29 1: */
9750 enum { MASK = 0x8000000b };
9751 /* Another example - taps at 32 31 30 10: */
9752 /* MASK = 0x00400007 */
9753
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009754 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009755 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009756 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009757
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009758 /* LCG has period of 2^32 and alternating lowest bit */
9759 random_LCG = 1664525 * random_LCG + 1013904223;
9760 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9761 t = (random_galois_LFSR << 1);
9762 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9763 t ^= MASK;
9764 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009765 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009766 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009767 * for $RANDOM range. Combining with subtraction is
9768 * just for fun. + and ^ would work equally well. */
9769 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009770 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009771 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009772 vrandom.flags &= ~VNOFUNC;
9773 } else {
9774 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009775 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009776 }
Eric Andersenef02f822004-03-11 13:34:24 +00009777}
Eric Andersen16767e22004-03-16 05:14:10 +00009778#endif
9779
Denis Vlasenko131ae172007-02-18 13:00:19 +00009780#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009781static int
Eric Andersenc470f442003-07-28 09:56:35 +00009782getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009783{
9784 char *p, *q;
9785 char c = '?';
9786 int done = 0;
9787 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009788 char s[12];
9789 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009790
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009791 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009792 return 1;
9793 optnext = optfirst + *param_optind - 1;
9794
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009795 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009796 p = NULL;
9797 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009798 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009799 if (p == NULL || *p == '\0') {
9800 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009801 p = *optnext;
9802 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009803 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009804 p = NULL;
9805 done = 1;
9806 goto out;
9807 }
9808 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009809 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009810 goto atend;
9811 }
9812
9813 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009814 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009815 if (*q == '\0') {
9816 if (optstr[0] == ':') {
9817 s[0] = c;
9818 s[1] = '\0';
9819 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009820 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009821 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009822 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009823 }
9824 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009825 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009826 }
9827 if (*++q == ':')
9828 q++;
9829 }
9830
9831 if (*++q == ':') {
9832 if (*p == '\0' && (p = *optnext) == NULL) {
9833 if (optstr[0] == ':') {
9834 s[0] = c;
9835 s[1] = '\0';
9836 err |= setvarsafe("OPTARG", s, 0);
9837 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009838 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009839 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009840 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009841 c = '?';
9842 }
Eric Andersenc470f442003-07-28 09:56:35 +00009843 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009844 }
9845
9846 if (p == *optnext)
9847 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009848 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009849 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009850 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009851 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009852 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009853 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009854 *param_optind = optnext - optfirst + 1;
9855 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009856 err |= setvarsafe("OPTIND", s, VNOFUNC);
9857 s[0] = c;
9858 s[1] = '\0';
9859 err |= setvarsafe(optvar, s, 0);
9860 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009861 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009862 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009863 flush_stdout_stderr();
9864 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009865 }
9866 return done;
9867}
Eric Andersenc470f442003-07-28 09:56:35 +00009868
9869/*
9870 * The getopts builtin. Shellparam.optnext points to the next argument
9871 * to be processed. Shellparam.optptr points to the next character to
9872 * be processed in the current argument. If shellparam.optnext is NULL,
9873 * then it's the first time getopts has been called.
9874 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009875static int
Eric Andersenc470f442003-07-28 09:56:35 +00009876getoptscmd(int argc, char **argv)
9877{
9878 char **optbase;
9879
9880 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009881 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009882 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009883 optbase = shellparam.p;
9884 if (shellparam.optind > shellparam.nparam + 1) {
9885 shellparam.optind = 1;
9886 shellparam.optoff = -1;
9887 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009888 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009889 optbase = &argv[3];
9890 if (shellparam.optind > argc - 2) {
9891 shellparam.optind = 1;
9892 shellparam.optoff = -1;
9893 }
9894 }
9895
9896 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009897 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009898}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009899#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009900
Eric Andersencb57d552001-06-28 07:25:16 +00009901
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009902/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009903
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009904struct heredoc {
9905 struct heredoc *next; /* next here document in list */
9906 union node *here; /* redirection node */
9907 char *eofmark; /* string indicating end of input */
9908 smallint striptabs; /* if set, strip leading tabs */
9909};
9910
9911static smallint tokpushback; /* last token pushed back */
9912static smallint parsebackquote; /* nonzero if we are inside backquotes */
9913static smallint quoteflag; /* set if (part of) last token was quoted */
9914static token_id_t lasttoken; /* last token read (integer id Txxx) */
9915static struct heredoc *heredoclist; /* list of here documents to read */
9916static char *wordtext; /* text of last word returned by readtoken */
9917static struct nodelist *backquotelist;
9918static union node *redirnode;
9919static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009920/*
9921 * NEOF is returned by parsecmd when it encounters an end of file. It
9922 * must be distinct from NULL, so we use the address of a variable that
9923 * happens to be handy.
9924 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009925#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009926
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009927static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009928static void
9929raise_error_syntax(const char *msg)
9930{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009931 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009932 /* NOTREACHED */
9933}
9934
9935/*
9936 * Called when an unexpected token is read during the parse. The argument
9937 * is the token that is expected, or -1 if more than one type of token can
9938 * occur at this point.
9939 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009940static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009941static void
9942raise_error_unexpected_syntax(int token)
9943{
9944 char msg[64];
9945 int l;
9946
9947 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9948 if (token >= 0)
9949 sprintf(msg + l, " (expecting %s)", tokname(token));
9950 raise_error_syntax(msg);
9951 /* NOTREACHED */
9952}
Eric Andersencb57d552001-06-28 07:25:16 +00009953
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009954#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009955
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009956/* parsing is heavily cross-recursive, need these forward decls */
9957static union node *andor(void);
9958static union node *pipeline(void);
9959static union node *parse_command(void);
9960static void parseheredoc(void);
9961static char peektoken(void);
9962static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009963
Eric Andersenc470f442003-07-28 09:56:35 +00009964static union node *
9965list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009966{
9967 union node *n1, *n2, *n3;
9968 int tok;
9969
Eric Andersenc470f442003-07-28 09:56:35 +00009970 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9971 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009972 return NULL;
9973 n1 = NULL;
9974 for (;;) {
9975 n2 = andor();
9976 tok = readtoken();
9977 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009978 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009979 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009980 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009981 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009982 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009983 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009984 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009985 n2 = n3;
9986 }
9987 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009988 }
9989 }
9990 if (n1 == NULL) {
9991 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009992 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009993 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009994 n3->type = NSEMI;
9995 n3->nbinary.ch1 = n1;
9996 n3->nbinary.ch2 = n2;
9997 n1 = n3;
9998 }
9999 switch (tok) {
10000 case TBACKGND:
10001 case TSEMI:
10002 tok = readtoken();
10003 /* fall through */
10004 case TNL:
10005 if (tok == TNL) {
10006 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010007 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010008 return n1;
10009 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010010 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010011 }
Eric Andersenc470f442003-07-28 09:56:35 +000010012 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010013 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010014 return n1;
10015 break;
10016 case TEOF:
10017 if (heredoclist)
10018 parseheredoc();
10019 else
Eric Andersenc470f442003-07-28 09:56:35 +000010020 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010021 return n1;
10022 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010023 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010024 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010025 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010026 return n1;
10027 }
10028 }
10029}
10030
Eric Andersenc470f442003-07-28 09:56:35 +000010031static union node *
10032andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010033{
Eric Andersencb57d552001-06-28 07:25:16 +000010034 union node *n1, *n2, *n3;
10035 int t;
10036
Eric Andersencb57d552001-06-28 07:25:16 +000010037 n1 = pipeline();
10038 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010039 t = readtoken();
10040 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010041 t = NAND;
10042 } else if (t == TOR) {
10043 t = NOR;
10044 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010045 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010046 return n1;
10047 }
Eric Andersenc470f442003-07-28 09:56:35 +000010048 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010049 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010050 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010051 n3->type = t;
10052 n3->nbinary.ch1 = n1;
10053 n3->nbinary.ch2 = n2;
10054 n1 = n3;
10055 }
10056}
10057
Eric Andersenc470f442003-07-28 09:56:35 +000010058static union node *
10059pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010060{
Eric Andersencb57d552001-06-28 07:25:16 +000010061 union node *n1, *n2, *pipenode;
10062 struct nodelist *lp, *prev;
10063 int negate;
10064
10065 negate = 0;
10066 TRACE(("pipeline: entered\n"));
10067 if (readtoken() == TNOT) {
10068 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010069 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010070 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010071 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010072 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010073 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010074 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010075 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010076 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010077 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010078 pipenode->npipe.cmdlist = lp;
10079 lp->n = n1;
10080 do {
10081 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010082 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010083 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010084 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010085 prev->next = lp;
10086 } while (readtoken() == TPIPE);
10087 lp->next = NULL;
10088 n1 = pipenode;
10089 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010090 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010091 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010092 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010093 n2->type = NNOT;
10094 n2->nnot.com = n1;
10095 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010096 }
10097 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010098}
10099
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010100static union node *
10101makename(void)
10102{
10103 union node *n;
10104
Denis Vlasenko597906c2008-02-20 16:38:54 +000010105 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010106 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010107 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010108 n->narg.text = wordtext;
10109 n->narg.backquote = backquotelist;
10110 return n;
10111}
10112
10113static void
10114fixredir(union node *n, const char *text, int err)
10115{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010116 int fd;
10117
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010118 TRACE(("Fix redir %s %d\n", text, err));
10119 if (!err)
10120 n->ndup.vname = NULL;
10121
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010122 fd = bb_strtou(text, NULL, 10);
10123 if (!errno && fd >= 0)
10124 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010125 else if (LONE_DASH(text))
10126 n->ndup.dupfd = -1;
10127 else {
10128 if (err)
10129 raise_error_syntax("Bad fd number");
10130 n->ndup.vname = makename();
10131 }
10132}
10133
10134/*
10135 * Returns true if the text contains nothing to expand (no dollar signs
10136 * or backquotes).
10137 */
10138static int
10139noexpand(char *text)
10140{
10141 char *p;
10142 char c;
10143
10144 p = text;
10145 while ((c = *p++) != '\0') {
10146 if (c == CTLQUOTEMARK)
10147 continue;
10148 if (c == CTLESC)
10149 p++;
10150 else if (SIT(c, BASESYNTAX) == CCTL)
10151 return 0;
10152 }
10153 return 1;
10154}
10155
10156static void
10157parsefname(void)
10158{
10159 union node *n = redirnode;
10160
10161 if (readtoken() != TWORD)
10162 raise_error_unexpected_syntax(-1);
10163 if (n->type == NHERE) {
10164 struct heredoc *here = heredoc;
10165 struct heredoc *p;
10166 int i;
10167
10168 if (quoteflag == 0)
10169 n->type = NXHERE;
10170 TRACE(("Here document %d\n", n->type));
10171 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10172 raise_error_syntax("Illegal eof marker for << redirection");
10173 rmescapes(wordtext);
10174 here->eofmark = wordtext;
10175 here->next = NULL;
10176 if (heredoclist == NULL)
10177 heredoclist = here;
10178 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010179 for (p = heredoclist; p->next; p = p->next)
10180 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010181 p->next = here;
10182 }
10183 } else if (n->type == NTOFD || n->type == NFROMFD) {
10184 fixredir(n, wordtext, 0);
10185 } else {
10186 n->nfile.fname = makename();
10187 }
10188}
Eric Andersencb57d552001-06-28 07:25:16 +000010189
Eric Andersenc470f442003-07-28 09:56:35 +000010190static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010191simplecmd(void)
10192{
10193 union node *args, **app;
10194 union node *n = NULL;
10195 union node *vars, **vpp;
10196 union node **rpp, *redir;
10197 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010198#if ENABLE_ASH_BASH_COMPAT
10199 smallint double_brackets_flag = 0;
10200#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010201
10202 args = NULL;
10203 app = &args;
10204 vars = NULL;
10205 vpp = &vars;
10206 redir = NULL;
10207 rpp = &redir;
10208
10209 savecheckkwd = CHKALIAS;
10210 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010211 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010212 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010213 t = readtoken();
10214 switch (t) {
10215#if ENABLE_ASH_BASH_COMPAT
10216 case TAND: /* "&&" */
10217 case TOR: /* "||" */
10218 if (!double_brackets_flag) {
10219 tokpushback = 1;
10220 goto out;
10221 }
10222 wordtext = (char *) (t == TAND ? "-a" : "-o");
10223#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010224 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010225 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010226 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010227 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010228 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010229#if ENABLE_ASH_BASH_COMPAT
10230 if (strcmp("[[", wordtext) == 0)
10231 double_brackets_flag = 1;
10232 else if (strcmp("]]", wordtext) == 0)
10233 double_brackets_flag = 0;
10234#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010235 n->narg.backquote = backquotelist;
10236 if (savecheckkwd && isassignment(wordtext)) {
10237 *vpp = n;
10238 vpp = &n->narg.next;
10239 } else {
10240 *app = n;
10241 app = &n->narg.next;
10242 savecheckkwd = 0;
10243 }
10244 break;
10245 case TREDIR:
10246 *rpp = n = redirnode;
10247 rpp = &n->nfile.next;
10248 parsefname(); /* read name of redirection file */
10249 break;
10250 case TLP:
10251 if (args && app == &args->narg.next
10252 && !vars && !redir
10253 ) {
10254 struct builtincmd *bcmd;
10255 const char *name;
10256
10257 /* We have a function */
10258 if (readtoken() != TRP)
10259 raise_error_unexpected_syntax(TRP);
10260 name = n->narg.text;
10261 if (!goodname(name)
10262 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10263 ) {
10264 raise_error_syntax("Bad function name");
10265 }
10266 n->type = NDEFUN;
10267 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10268 n->narg.next = parse_command();
10269 return n;
10270 }
10271 /* fall through */
10272 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010273 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010274 goto out;
10275 }
10276 }
10277 out:
10278 *app = NULL;
10279 *vpp = NULL;
10280 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010281 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010282 n->type = NCMD;
10283 n->ncmd.args = args;
10284 n->ncmd.assign = vars;
10285 n->ncmd.redirect = redir;
10286 return n;
10287}
10288
10289static union node *
10290parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010291{
Eric Andersencb57d552001-06-28 07:25:16 +000010292 union node *n1, *n2;
10293 union node *ap, **app;
10294 union node *cp, **cpp;
10295 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010296 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010297 int t;
10298
10299 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010300 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010301
Eric Andersencb57d552001-06-28 07:25:16 +000010302 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010303 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010304 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010305 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010306 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010307 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010308 n1->type = NIF;
10309 n1->nif.test = list(0);
10310 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010311 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010312 n1->nif.ifpart = list(0);
10313 n2 = n1;
10314 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010315 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010316 n2 = n2->nif.elsepart;
10317 n2->type = NIF;
10318 n2->nif.test = list(0);
10319 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010320 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010321 n2->nif.ifpart = list(0);
10322 }
10323 if (lasttoken == TELSE)
10324 n2->nif.elsepart = list(0);
10325 else {
10326 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010327 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010328 }
Eric Andersenc470f442003-07-28 09:56:35 +000010329 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010330 break;
10331 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010332 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010333 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010334 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010335 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010336 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010337 got = readtoken();
10338 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010339 TRACE(("expecting DO got %s %s\n", tokname(got),
10340 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010341 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010342 }
10343 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010344 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010345 break;
10346 }
10347 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010348 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010349 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010350 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010351 n1->type = NFOR;
10352 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010353 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010354 if (readtoken() == TIN) {
10355 app = &ap;
10356 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010357 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010358 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010359 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010360 n2->narg.text = wordtext;
10361 n2->narg.backquote = backquotelist;
10362 *app = n2;
10363 app = &n2->narg.next;
10364 }
10365 *app = NULL;
10366 n1->nfor.args = ap;
10367 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010368 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010369 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010370 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010371 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010372 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010373 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010374 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010375 n1->nfor.args = n2;
10376 /*
10377 * Newline or semicolon here is optional (but note
10378 * that the original Bourne shell only allowed NL).
10379 */
10380 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010381 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010382 }
Eric Andersenc470f442003-07-28 09:56:35 +000010383 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010384 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010385 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010386 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010387 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010388 break;
10389 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010390 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010391 n1->type = NCASE;
10392 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010393 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010394 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010395 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010396 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010397 n2->narg.text = wordtext;
10398 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010399 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010400 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010401 } while (readtoken() == TNL);
10402 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010403 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010404 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010405 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010406 checkkwd = CHKNL | CHKKWD;
10407 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010408 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010409 if (lasttoken == TLP)
10410 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010411 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010412 cp->type = NCLIST;
10413 app = &cp->nclist.pattern;
10414 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010415 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010416 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010417 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010418 ap->narg.text = wordtext;
10419 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010420 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010421 break;
10422 app = &ap->narg.next;
10423 readtoken();
10424 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010425 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010426 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010427 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010428 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010429
Eric Andersenc470f442003-07-28 09:56:35 +000010430 cpp = &cp->nclist.next;
10431
10432 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010433 t = readtoken();
10434 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010435 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010436 raise_error_unexpected_syntax(TENDCASE);
10437 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010438 }
Eric Andersenc470f442003-07-28 09:56:35 +000010439 }
Eric Andersencb57d552001-06-28 07:25:16 +000010440 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010441 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010442 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010443 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010444 n1->type = NSUBSHELL;
10445 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010446 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010447 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010448 break;
10449 case TBEGIN:
10450 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010451 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010452 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010453 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010454 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010455 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010456 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010457 }
10458
Eric Andersenc470f442003-07-28 09:56:35 +000010459 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010460 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010461
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010462 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010463 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010464 checkkwd = CHKKWD | CHKALIAS;
10465 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010466 while (readtoken() == TREDIR) {
10467 *rpp = n2 = redirnode;
10468 rpp = &n2->nfile.next;
10469 parsefname();
10470 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010471 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010472 *rpp = NULL;
10473 if (redir) {
10474 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010475 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010476 n2->type = NREDIR;
10477 n2->nredir.n = n1;
10478 n1 = n2;
10479 }
10480 n1->nredir.redirect = redir;
10481 }
Eric Andersencb57d552001-06-28 07:25:16 +000010482 return n1;
10483}
10484
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010485#if ENABLE_ASH_BASH_COMPAT
10486static int decode_dollar_squote(void)
10487{
10488 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10489 int c, cnt;
10490 char *p;
10491 char buf[4];
10492
10493 c = pgetc();
10494 p = strchr(C_escapes, c);
10495 if (p) {
10496 buf[0] = c;
10497 p = buf;
10498 cnt = 3;
10499 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10500 do {
10501 c = pgetc();
10502 *++p = c;
10503 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10504 pungetc();
10505 } else if (c == 'x') { /* \xHH */
10506 do {
10507 c = pgetc();
10508 *++p = c;
10509 } while (isxdigit(c) && --cnt);
10510 pungetc();
10511 if (cnt == 3) { /* \x but next char is "bad" */
10512 c = 'x';
10513 goto unrecognized;
10514 }
10515 } else { /* simple seq like \\ or \t */
10516 p++;
10517 }
10518 *p = '\0';
10519 p = buf;
10520 c = bb_process_escape_sequence((void*)&p);
10521 } else { /* unrecognized "\z": print both chars unless ' or " */
10522 if (c != '\'' && c != '"') {
10523 unrecognized:
10524 c |= 0x100; /* "please encode \, then me" */
10525 }
10526 }
10527 return c;
10528}
10529#endif
10530
Eric Andersencb57d552001-06-28 07:25:16 +000010531/*
10532 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10533 * is not NULL, read a here document. In the latter case, eofmark is the
10534 * word which marks the end of the document and striptabs is true if
10535 * leading tabs should be stripped from the document. The argument firstc
10536 * is the first character of the input token or document.
10537 *
10538 * Because C does not have internal subroutines, I have simulated them
10539 * using goto's to implement the subroutine linkage. The following macros
10540 * will run code that appears at the end of readtoken1.
10541 */
Eric Andersen2870d962001-07-02 17:27:21 +000010542#define CHECKEND() {goto checkend; checkend_return:;}
10543#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10544#define PARSESUB() {goto parsesub; parsesub_return:;}
10545#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10546#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10547#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010548static int
Eric Andersenc470f442003-07-28 09:56:35 +000010549readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010550{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010551 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010552 int c = firstc;
10553 char *out;
10554 int len;
10555 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010556 struct nodelist *bqlist;
10557 smallint quotef;
10558 smallint dblquote;
10559 smallint oldstyle;
10560 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010561#if ENABLE_ASH_EXPAND_PRMT
10562 smallint pssyntax; /* we are expanding a prompt string */
10563#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010564 int varnest; /* levels of variables expansion */
10565 int arinest; /* levels of arithmetic expansion */
10566 int parenlevel; /* levels of parens in arithmetic */
10567 int dqvarnest; /* levels of variables expansion within double quotes */
10568
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010569 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10570
Eric Andersencb57d552001-06-28 07:25:16 +000010571#if __GNUC__
10572 /* Avoid longjmp clobbering */
10573 (void) &out;
10574 (void) &quotef;
10575 (void) &dblquote;
10576 (void) &varnest;
10577 (void) &arinest;
10578 (void) &parenlevel;
10579 (void) &dqvarnest;
10580 (void) &oldstyle;
10581 (void) &prevsyntax;
10582 (void) &syntax;
10583#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010584 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010585 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010586 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010587 oldstyle = 0;
10588 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010589#if ENABLE_ASH_EXPAND_PRMT
10590 pssyntax = (syntax == PSSYNTAX);
10591 if (pssyntax)
10592 syntax = DQSYNTAX;
10593#endif
10594 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010595 varnest = 0;
10596 arinest = 0;
10597 parenlevel = 0;
10598 dqvarnest = 0;
10599
10600 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010601 loop: { /* for each line, until end of word */
10602 CHECKEND(); /* set c to PEOF if at end of here document */
10603 for (;;) { /* until end of line or end of word */
10604 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010605 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010606 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010607 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010608 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010609 USTPUTC(c, out);
10610 plinno++;
10611 if (doprompt)
10612 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010613 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010614 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010615 case CWORD:
10616 USTPUTC(c, out);
10617 break;
10618 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010619 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010620 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010621#if ENABLE_ASH_BASH_COMPAT
10622 if (c == '\\' && bash_dollar_squote) {
10623 c = decode_dollar_squote();
10624 if (c & 0x100) {
10625 USTPUTC('\\', out);
10626 c = (unsigned char)c;
10627 }
10628 }
10629#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010630 USTPUTC(c, out);
10631 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010632 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010633 c = pgetc2();
10634 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010635 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010636 USTPUTC('\\', out);
10637 pungetc();
10638 } else if (c == '\n') {
10639 if (doprompt)
10640 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010641 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010642#if ENABLE_ASH_EXPAND_PRMT
10643 if (c == '$' && pssyntax) {
10644 USTPUTC(CTLESC, out);
10645 USTPUTC('\\', out);
10646 }
10647#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010648 if (dblquote && c != '\\'
10649 && c != '`' && c != '$'
10650 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010651 ) {
10652 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010653 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010654 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010655 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010656 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010657 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010658 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010659 }
10660 break;
10661 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010662 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010663 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010664 if (eofmark == NULL) {
10665 USTPUTC(CTLQUOTEMARK, out);
10666 }
Eric Andersencb57d552001-06-28 07:25:16 +000010667 break;
10668 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010669 syntax = DQSYNTAX;
10670 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010671 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010672 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010673 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010674 if (eofmark != NULL && arinest == 0
10675 && varnest == 0
10676 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010677 USTPUTC(c, out);
10678 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010679 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010680 syntax = BASESYNTAX;
10681 dblquote = 0;
10682 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010683 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010684 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010685 }
10686 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010687 case CVAR: /* '$' */
10688 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010689 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010690 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010691 if (varnest > 0) {
10692 varnest--;
10693 if (dqvarnest > 0) {
10694 dqvarnest--;
10695 }
10696 USTPUTC(CTLENDVAR, out);
10697 } else {
10698 USTPUTC(c, out);
10699 }
10700 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010701#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010702 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010703 parenlevel++;
10704 USTPUTC(c, out);
10705 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010706 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010707 if (parenlevel > 0) {
10708 USTPUTC(c, out);
10709 --parenlevel;
10710 } else {
10711 if (pgetc() == ')') {
10712 if (--arinest == 0) {
10713 USTPUTC(CTLENDARI, out);
10714 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010715 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010716 } else
10717 USTPUTC(')', out);
10718 } else {
10719 /*
10720 * unbalanced parens
10721 * (don't 2nd guess - no error)
10722 */
10723 pungetc();
10724 USTPUTC(')', out);
10725 }
10726 }
10727 break;
10728#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010729 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010730 PARSEBACKQOLD();
10731 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010732 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010733 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010734 case CIGN:
10735 break;
10736 default:
10737 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010738 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010739#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010740 if (c != PEOA)
10741#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010742 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010743
Eric Andersencb57d552001-06-28 07:25:16 +000010744 }
10745 c = pgetc_macro();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010746 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010747 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010748 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010749#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010750 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010751 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010752#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010753 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010754 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010755 if (varnest != 0) {
10756 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010757 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010758 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010759 }
10760 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010761 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010762 out = stackblock();
10763 if (eofmark == NULL) {
Denis Vlasenko5a867312008-07-24 19:46:38 +000010764 if ((c == '>' || c == '<') && quotef == 0) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010765 int maxlen = 9 + 1; /* max 9 digit fd#: 999999999 */
10766 char *np = out;
10767 while (--maxlen && isdigit(*np))
10768 np++;
10769 if (*np == '\0') {
10770 PARSEREDIR(); /* passed as params: out, c */
10771 lasttoken = TREDIR;
10772 return lasttoken;
10773 }
10774 /* else: non-number X seen, interpret it
10775 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010776 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010777 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010778 }
10779 quoteflag = quotef;
10780 backquotelist = bqlist;
10781 grabstackblock(len);
10782 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010783 lasttoken = TWORD;
10784 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010785/* end of readtoken routine */
10786
Eric Andersencb57d552001-06-28 07:25:16 +000010787/*
10788 * Check to see whether we are at the end of the here document. When this
10789 * is called, c is set to the first character of the next input line. If
10790 * we are at the end of the here document, this routine sets the c to PEOF.
10791 */
Eric Andersenc470f442003-07-28 09:56:35 +000010792checkend: {
10793 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010794#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010795 if (c == PEOA) {
10796 c = pgetc2();
10797 }
10798#endif
10799 if (striptabs) {
10800 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010801 c = pgetc2();
10802 }
Eric Andersenc470f442003-07-28 09:56:35 +000010803 }
10804 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010805 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010806 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010807
Eric Andersenc470f442003-07-28 09:56:35 +000010808 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010809 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10810 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010811 if (*p == '\n' && *q == '\0') {
10812 c = PEOF;
10813 plinno++;
10814 needprompt = doprompt;
10815 } else {
10816 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010817 }
10818 }
10819 }
10820 }
Eric Andersenc470f442003-07-28 09:56:35 +000010821 goto checkend_return;
10822}
Eric Andersencb57d552001-06-28 07:25:16 +000010823
Eric Andersencb57d552001-06-28 07:25:16 +000010824/*
10825 * Parse a redirection operator. The variable "out" points to a string
10826 * specifying the fd to be redirected. The variable "c" contains the
10827 * first character of the redirection operator.
10828 */
Eric Andersenc470f442003-07-28 09:56:35 +000010829parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010830 /* out is already checked to be a valid number or "" */
10831 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010832 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010833
Denis Vlasenko597906c2008-02-20 16:38:54 +000010834 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010835 if (c == '>') {
10836 np->nfile.fd = 1;
10837 c = pgetc();
10838 if (c == '>')
10839 np->type = NAPPEND;
10840 else if (c == '|')
10841 np->type = NCLOBBER;
10842 else if (c == '&')
10843 np->type = NTOFD;
10844 else {
10845 np->type = NTO;
10846 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010847 }
Eric Andersenc470f442003-07-28 09:56:35 +000010848 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010849 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010850 c = pgetc();
10851 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010852 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010853 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010854 np = stzalloc(sizeof(struct nhere));
10855 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010856 }
10857 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010858 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010859 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010860 c = pgetc();
10861 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010862 heredoc->striptabs = 1;
10863 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010864 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010865 pungetc();
10866 }
10867 break;
10868
10869 case '&':
10870 np->type = NFROMFD;
10871 break;
10872
10873 case '>':
10874 np->type = NFROMTO;
10875 break;
10876
10877 default:
10878 np->type = NFROM;
10879 pungetc();
10880 break;
10881 }
Eric Andersencb57d552001-06-28 07:25:16 +000010882 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010883 if (fd >= 0)
10884 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010885 redirnode = np;
10886 goto parseredir_return;
10887}
Eric Andersencb57d552001-06-28 07:25:16 +000010888
Eric Andersencb57d552001-06-28 07:25:16 +000010889/*
10890 * Parse a substitution. At this point, we have read the dollar sign
10891 * and nothing else.
10892 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010893
10894/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10895 * (assuming ascii char codes, as the original implementation did) */
10896#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010897 (((unsigned)(c) - 33 < 32) \
10898 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010899parsesub: {
10900 int subtype;
10901 int typeloc;
10902 int flags;
10903 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010904 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010905
Eric Andersenc470f442003-07-28 09:56:35 +000010906 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010907 if (c <= PEOA_OR_PEOF
10908 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010909 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010910#if ENABLE_ASH_BASH_COMPAT
10911 if (c == '\'')
10912 bash_dollar_squote = 1;
10913 else
10914#endif
10915 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010916 pungetc();
10917 } else if (c == '(') { /* $(command) or $((arith)) */
10918 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010919#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010920 PARSEARITH();
10921#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010922 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010923#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010924 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010925 pungetc();
10926 PARSEBACKQNEW();
10927 }
10928 } else {
10929 USTPUTC(CTLVAR, out);
10930 typeloc = out - (char *)stackblock();
10931 USTPUTC(VSNORMAL, out);
10932 subtype = VSNORMAL;
10933 if (c == '{') {
10934 c = pgetc();
10935 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010936 c = pgetc();
10937 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010938 c = '#';
10939 else
10940 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010941 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010942 subtype = 0;
10943 }
10944 if (c > PEOA_OR_PEOF && is_name(c)) {
10945 do {
10946 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010947 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010948 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010949 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010950 do {
10951 STPUTC(c, out);
10952 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010953 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010954 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010955 USTPUTC(c, out);
10956 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010957 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010958 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010959
Eric Andersenc470f442003-07-28 09:56:35 +000010960 STPUTC('=', out);
10961 flags = 0;
10962 if (subtype == 0) {
10963 switch (c) {
10964 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010965 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010966#if ENABLE_ASH_BASH_COMPAT
10967 if (c == ':' || c == '$' || isdigit(c)) {
10968 pungetc();
10969 subtype = VSSUBSTR;
10970 break;
10971 }
10972#endif
10973 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010974 /*FALLTHROUGH*/
10975 default:
10976 p = strchr(types, c);
10977 if (p == NULL)
10978 goto badsub;
10979 subtype = p - types + VSNORMAL;
10980 break;
10981 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010982 case '#': {
10983 int cc = c;
10984 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10985 c = pgetc();
10986 if (c == cc)
10987 subtype++;
10988 else
10989 pungetc();
10990 break;
10991 }
10992#if ENABLE_ASH_BASH_COMPAT
10993 case '/':
10994 subtype = VSREPLACE;
10995 c = pgetc();
10996 if (c == '/')
10997 subtype++; /* VSREPLACEALL */
10998 else
10999 pungetc();
11000 break;
11001#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011002 }
Eric Andersenc470f442003-07-28 09:56:35 +000011003 } else {
11004 pungetc();
11005 }
11006 if (dblquote || arinest)
11007 flags |= VSQUOTE;
11008 *((char *)stackblock() + typeloc) = subtype | flags;
11009 if (subtype != VSNORMAL) {
11010 varnest++;
11011 if (dblquote || arinest) {
11012 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011013 }
11014 }
11015 }
Eric Andersenc470f442003-07-28 09:56:35 +000011016 goto parsesub_return;
11017}
Eric Andersencb57d552001-06-28 07:25:16 +000011018
Eric Andersencb57d552001-06-28 07:25:16 +000011019/*
11020 * Called to parse command substitutions. Newstyle is set if the command
11021 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11022 * list of commands (passed by reference), and savelen is the number of
11023 * characters on the top of the stack which must be preserved.
11024 */
Eric Andersenc470f442003-07-28 09:56:35 +000011025parsebackq: {
11026 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011027 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011028 union node *n;
11029 char *volatile str;
11030 struct jmploc jmploc;
11031 struct jmploc *volatile savehandler;
11032 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011033 smallint saveprompt = 0;
11034
Eric Andersencb57d552001-06-28 07:25:16 +000011035#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011036 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011037#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011038 savepbq = parsebackquote;
11039 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011040 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011041 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011042 exception_handler = savehandler;
11043 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011044 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011045 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011046 str = NULL;
11047 savelen = out - (char *)stackblock();
11048 if (savelen > 0) {
11049 str = ckmalloc(savelen);
11050 memcpy(str, stackblock(), savelen);
11051 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011052 savehandler = exception_handler;
11053 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011054 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011055 if (oldstyle) {
11056 /* We must read until the closing backquote, giving special
11057 treatment to some slashes, and then push the string and
11058 reread it as input, interpreting it normally. */
11059 char *pout;
11060 int pc;
11061 size_t psavelen;
11062 char *pstr;
11063
11064
11065 STARTSTACKSTR(pout);
11066 for (;;) {
11067 if (needprompt) {
11068 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011069 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011070 pc = pgetc();
11071 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011072 case '`':
11073 goto done;
11074
11075 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011076 pc = pgetc();
11077 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000011078 plinno++;
11079 if (doprompt)
11080 setprompt(2);
11081 /*
11082 * If eating a newline, avoid putting
11083 * the newline into the new character
11084 * stream (via the STPUTC after the
11085 * switch).
11086 */
11087 continue;
11088 }
11089 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011090 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011091 STPUTC('\\', pout);
11092 if (pc > PEOA_OR_PEOF) {
11093 break;
11094 }
11095 /* fall through */
11096
11097 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011098#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011099 case PEOA:
11100#endif
11101 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011102 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011103
11104 case '\n':
11105 plinno++;
11106 needprompt = doprompt;
11107 break;
11108
11109 default:
11110 break;
11111 }
11112 STPUTC(pc, pout);
11113 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011114 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011115 STPUTC('\0', pout);
11116 psavelen = pout - (char *)stackblock();
11117 if (psavelen > 0) {
11118 pstr = grabstackstr(pout);
11119 setinputstring(pstr);
11120 }
11121 }
11122 nlpp = &bqlist;
11123 while (*nlpp)
11124 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011125 *nlpp = stzalloc(sizeof(**nlpp));
11126 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011127 parsebackquote = oldstyle;
11128
11129 if (oldstyle) {
11130 saveprompt = doprompt;
11131 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011132 }
11133
Eric Andersenc470f442003-07-28 09:56:35 +000011134 n = list(2);
11135
11136 if (oldstyle)
11137 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011138 else if (readtoken() != TRP)
11139 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011140
11141 (*nlpp)->n = n;
11142 if (oldstyle) {
11143 /*
11144 * Start reading from old file again, ignoring any pushed back
11145 * tokens left from the backquote parsing
11146 */
11147 popfile();
11148 tokpushback = 0;
11149 }
11150 while (stackblocksize() <= savelen)
11151 growstackblock();
11152 STARTSTACKSTR(out);
11153 if (str) {
11154 memcpy(out, str, savelen);
11155 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011156 INT_OFF;
11157 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011158 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011159 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011160 }
11161 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011162 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011163 if (arinest || dblquote)
11164 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11165 else
11166 USTPUTC(CTLBACKQ, out);
11167 if (oldstyle)
11168 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011169 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011170}
11171
Denis Vlasenko131ae172007-02-18 13:00:19 +000011172#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011173/*
11174 * Parse an arithmetic expansion (indicate start of one and set state)
11175 */
Eric Andersenc470f442003-07-28 09:56:35 +000011176parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011177 if (++arinest == 1) {
11178 prevsyntax = syntax;
11179 syntax = ARISYNTAX;
11180 USTPUTC(CTLARI, out);
11181 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011182 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011183 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011184 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011185 } else {
11186 /*
11187 * we collapse embedded arithmetic expansion to
11188 * parenthesis, which should be equivalent
11189 */
11190 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011191 }
Eric Andersenc470f442003-07-28 09:56:35 +000011192 goto parsearith_return;
11193}
11194#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011195
Eric Andersenc470f442003-07-28 09:56:35 +000011196} /* end of readtoken */
11197
Eric Andersencb57d552001-06-28 07:25:16 +000011198/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011199 * Read the next input token.
11200 * If the token is a word, we set backquotelist to the list of cmds in
11201 * backquotes. We set quoteflag to true if any part of the word was
11202 * quoted.
11203 * If the token is TREDIR, then we set redirnode to a structure containing
11204 * the redirection.
11205 * In all cases, the variable startlinno is set to the number of the line
11206 * on which the token starts.
11207 *
11208 * [Change comment: here documents and internal procedures]
11209 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11210 * word parsing code into a separate routine. In this case, readtoken
11211 * doesn't need to have any internal procedures, but parseword does.
11212 * We could also make parseoperator in essence the main routine, and
11213 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011214 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011215#define NEW_xxreadtoken
11216#ifdef NEW_xxreadtoken
11217/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011218static const char xxreadtoken_chars[7] ALIGN1 = {
11219 '\n', '(', ')', '&', '|', ';', 0
11220};
Eric Andersencb57d552001-06-28 07:25:16 +000011221
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011222static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011223 TNL, TLP, TRP, /* only single occurrence allowed */
11224 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11225 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011226 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011227};
11228
11229#define xxreadtoken_doubles \
11230 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11231#define xxreadtoken_singles \
11232 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11233
11234static int
11235xxreadtoken(void)
11236{
11237 int c;
11238
11239 if (tokpushback) {
11240 tokpushback = 0;
11241 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011242 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011243 if (needprompt) {
11244 setprompt(2);
11245 }
11246 startlinno = plinno;
11247 for (;;) { /* until token or start of word found */
11248 c = pgetc_macro();
11249
11250 if ((c != ' ') && (c != '\t')
11251#if ENABLE_ASH_ALIAS
11252 && (c != PEOA)
11253#endif
11254 ) {
11255 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011256 while ((c = pgetc()) != '\n' && c != PEOF)
11257 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011258 pungetc();
11259 } else if (c == '\\') {
11260 if (pgetc() != '\n') {
11261 pungetc();
11262 goto READTOKEN1;
11263 }
11264 startlinno = ++plinno;
11265 if (doprompt)
11266 setprompt(2);
11267 } else {
11268 const char *p
11269 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11270
11271 if (c != PEOF) {
11272 if (c == '\n') {
11273 plinno++;
11274 needprompt = doprompt;
11275 }
11276
11277 p = strchr(xxreadtoken_chars, c);
11278 if (p == NULL) {
11279 READTOKEN1:
11280 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11281 }
11282
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011283 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011284 if (pgetc() == *p) { /* double occurrence? */
11285 p += xxreadtoken_doubles + 1;
11286 } else {
11287 pungetc();
11288 }
11289 }
11290 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011291 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11292 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011293 }
11294 }
11295 } /* for */
11296}
11297#else
11298#define RETURN(token) return lasttoken = token
11299static int
11300xxreadtoken(void)
11301{
11302 int c;
11303
11304 if (tokpushback) {
11305 tokpushback = 0;
11306 return lasttoken;
11307 }
11308 if (needprompt) {
11309 setprompt(2);
11310 }
11311 startlinno = plinno;
11312 for (;;) { /* until token or start of word found */
11313 c = pgetc_macro();
11314 switch (c) {
11315 case ' ': case '\t':
11316#if ENABLE_ASH_ALIAS
11317 case PEOA:
11318#endif
11319 continue;
11320 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011321 while ((c = pgetc()) != '\n' && c != PEOF)
11322 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011323 pungetc();
11324 continue;
11325 case '\\':
11326 if (pgetc() == '\n') {
11327 startlinno = ++plinno;
11328 if (doprompt)
11329 setprompt(2);
11330 continue;
11331 }
11332 pungetc();
11333 goto breakloop;
11334 case '\n':
11335 plinno++;
11336 needprompt = doprompt;
11337 RETURN(TNL);
11338 case PEOF:
11339 RETURN(TEOF);
11340 case '&':
11341 if (pgetc() == '&')
11342 RETURN(TAND);
11343 pungetc();
11344 RETURN(TBACKGND);
11345 case '|':
11346 if (pgetc() == '|')
11347 RETURN(TOR);
11348 pungetc();
11349 RETURN(TPIPE);
11350 case ';':
11351 if (pgetc() == ';')
11352 RETURN(TENDCASE);
11353 pungetc();
11354 RETURN(TSEMI);
11355 case '(':
11356 RETURN(TLP);
11357 case ')':
11358 RETURN(TRP);
11359 default:
11360 goto breakloop;
11361 }
11362 }
11363 breakloop:
11364 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11365#undef RETURN
11366}
11367#endif /* NEW_xxreadtoken */
11368
11369static int
11370readtoken(void)
11371{
11372 int t;
11373#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011374 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011375#endif
11376
11377#if ENABLE_ASH_ALIAS
11378 top:
11379#endif
11380
11381 t = xxreadtoken();
11382
11383 /*
11384 * eat newlines
11385 */
11386 if (checkkwd & CHKNL) {
11387 while (t == TNL) {
11388 parseheredoc();
11389 t = xxreadtoken();
11390 }
11391 }
11392
11393 if (t != TWORD || quoteflag) {
11394 goto out;
11395 }
11396
11397 /*
11398 * check for keywords
11399 */
11400 if (checkkwd & CHKKWD) {
11401 const char *const *pp;
11402
11403 pp = findkwd(wordtext);
11404 if (pp) {
11405 lasttoken = t = pp - tokname_array;
11406 TRACE(("keyword %s recognized\n", tokname(t)));
11407 goto out;
11408 }
11409 }
11410
11411 if (checkkwd & CHKALIAS) {
11412#if ENABLE_ASH_ALIAS
11413 struct alias *ap;
11414 ap = lookupalias(wordtext, 1);
11415 if (ap != NULL) {
11416 if (*ap->val) {
11417 pushstring(ap->val, ap);
11418 }
11419 goto top;
11420 }
11421#endif
11422 }
11423 out:
11424 checkkwd = 0;
11425#if DEBUG
11426 if (!alreadyseen)
11427 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11428 else
11429 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11430#endif
11431 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011432}
11433
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011434static char
11435peektoken(void)
11436{
11437 int t;
11438
11439 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011440 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011441 return tokname_array[t][0];
11442}
Eric Andersencb57d552001-06-28 07:25:16 +000011443
11444/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011445 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11446 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011447 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011448static union node *
11449parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011450{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011451 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011452
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011453 tokpushback = 0;
11454 doprompt = interact;
11455 if (doprompt)
11456 setprompt(doprompt);
11457 needprompt = 0;
11458 t = readtoken();
11459 if (t == TEOF)
11460 return NEOF;
11461 if (t == TNL)
11462 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011463 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011464 return list(1);
11465}
11466
11467/*
11468 * Input any here documents.
11469 */
11470static void
11471parseheredoc(void)
11472{
11473 struct heredoc *here;
11474 union node *n;
11475
11476 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011477 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011478
11479 while (here) {
11480 if (needprompt) {
11481 setprompt(2);
11482 }
11483 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11484 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011485 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011486 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011487 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011488 n->narg.text = wordtext;
11489 n->narg.backquote = backquotelist;
11490 here->here->nhere.doc = n;
11491 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011492 }
Eric Andersencb57d552001-06-28 07:25:16 +000011493}
11494
11495
11496/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011497 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011498 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011499#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011500static const char *
11501expandstr(const char *ps)
11502{
11503 union node n;
11504
11505 /* XXX Fix (char *) cast. */
11506 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011507 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011508 popfile();
11509
11510 n.narg.type = NARG;
11511 n.narg.next = NULL;
11512 n.narg.text = wordtext;
11513 n.narg.backquote = backquotelist;
11514
11515 expandarg(&n, NULL, 0);
11516 return stackblock();
11517}
11518#endif
11519
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011520/*
11521 * Execute a command or commands contained in a string.
11522 */
11523static int
11524evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011525{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011526 union node *n;
11527 struct stackmark smark;
11528 int skip;
11529
11530 setinputstring(s);
11531 setstackmark(&smark);
11532
11533 skip = 0;
11534 while ((n = parsecmd(0)) != NEOF) {
11535 evaltree(n, 0);
11536 popstackmark(&smark);
11537 skip = evalskip;
11538 if (skip)
11539 break;
11540 }
11541 popfile();
11542
11543 skip &= mask;
11544 evalskip = skip;
11545 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011546}
11547
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011548/*
11549 * The eval command.
11550 */
11551static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011552evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011553{
11554 char *p;
11555 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011556
Denis Vlasenko68404f12008-03-17 09:00:54 +000011557 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011558 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011559 argv += 2;
11560 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011561 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011562 for (;;) {
11563 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011564 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011565 if (p == NULL)
11566 break;
11567 STPUTC(' ', concat);
11568 }
11569 STPUTC('\0', concat);
11570 p = grabstackstr(concat);
11571 }
11572 evalstring(p, ~SKIPEVAL);
11573
11574 }
11575 return exitstatus;
11576}
11577
11578/*
11579 * Read and execute commands. "Top" is nonzero for the top level command
11580 * loop; it turns on prompting if the shell is interactive.
11581 */
11582static int
11583cmdloop(int top)
11584{
11585 union node *n;
11586 struct stackmark smark;
11587 int inter;
11588 int numeof = 0;
11589
11590 TRACE(("cmdloop(%d) called\n", top));
11591 for (;;) {
11592 int skip;
11593
11594 setstackmark(&smark);
11595#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011596 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011597 showjobs(stderr, SHOW_CHANGED);
11598#endif
11599 inter = 0;
11600 if (iflag && top) {
11601 inter++;
11602#if ENABLE_ASH_MAIL
11603 chkmail();
11604#endif
11605 }
11606 n = parsecmd(inter);
11607 /* showtree(n); DEBUG */
11608 if (n == NEOF) {
11609 if (!top || numeof >= 50)
11610 break;
11611 if (!stoppedjobs()) {
11612 if (!Iflag)
11613 break;
11614 out2str("\nUse \"exit\" to leave shell.\n");
11615 }
11616 numeof++;
11617 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011618 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11619 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011620 numeof = 0;
11621 evaltree(n, 0);
11622 }
11623 popstackmark(&smark);
11624 skip = evalskip;
11625
11626 if (skip) {
11627 evalskip = 0;
11628 return skip & SKIPEVAL;
11629 }
11630 }
11631 return 0;
11632}
11633
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011634/*
11635 * Take commands from a file. To be compatible we should do a path
11636 * search for the file, which is necessary to find sub-commands.
11637 */
11638static char *
11639find_dot_file(char *name)
11640{
11641 char *fullname;
11642 const char *path = pathval();
11643 struct stat statb;
11644
11645 /* don't try this for absolute or relative paths */
11646 if (strchr(name, '/'))
11647 return name;
11648
11649 while ((fullname = padvance(&path, name)) != NULL) {
11650 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11651 /*
11652 * Don't bother freeing here, since it will
11653 * be freed by the caller.
11654 */
11655 return fullname;
11656 }
11657 stunalloc(fullname);
11658 }
11659
11660 /* not found in the PATH */
11661 ash_msg_and_raise_error("%s: not found", name);
11662 /* NOTREACHED */
11663}
11664
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011665static int
11666dotcmd(int argc, char **argv)
11667{
11668 struct strlist *sp;
11669 volatile struct shparam saveparam;
11670 int status = 0;
11671
11672 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011673 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011674
Denis Vlasenko68404f12008-03-17 09:00:54 +000011675 if (argv[1]) { /* That's what SVR2 does */
11676 char *fullname = find_dot_file(argv[1]);
11677 argv += 2;
11678 argc -= 2;
11679 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011680 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011681 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011682 shellparam.nparam = argc;
11683 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011684 };
11685
11686 setinputfile(fullname, INPUT_PUSH_FILE);
11687 commandname = fullname;
11688 cmdloop(0);
11689 popfile();
11690
Denis Vlasenko68404f12008-03-17 09:00:54 +000011691 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011692 freeparam(&shellparam);
11693 shellparam = saveparam;
11694 };
11695 status = exitstatus;
11696 }
11697 return status;
11698}
11699
11700static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011701exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011702{
11703 if (stoppedjobs())
11704 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011705 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011706 exitstatus = number(argv[1]);
11707 raise_exception(EXEXIT);
11708 /* NOTREACHED */
11709}
11710
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011711/*
11712 * Read a file containing shell functions.
11713 */
11714static void
11715readcmdfile(char *name)
11716{
11717 setinputfile(name, INPUT_PUSH_FILE);
11718 cmdloop(0);
11719 popfile();
11720}
11721
11722
Denis Vlasenkocc571512007-02-23 21:10:35 +000011723/* ============ find_command inplementation */
11724
11725/*
11726 * Resolve a command name. If you change this routine, you may have to
11727 * change the shellexec routine as well.
11728 */
11729static void
11730find_command(char *name, struct cmdentry *entry, int act, const char *path)
11731{
11732 struct tblentry *cmdp;
11733 int idx;
11734 int prev;
11735 char *fullname;
11736 struct stat statb;
11737 int e;
11738 int updatetbl;
11739 struct builtincmd *bcmd;
11740
11741 /* If name contains a slash, don't use PATH or hash table */
11742 if (strchr(name, '/') != NULL) {
11743 entry->u.index = -1;
11744 if (act & DO_ABS) {
11745 while (stat(name, &statb) < 0) {
11746#ifdef SYSV
11747 if (errno == EINTR)
11748 continue;
11749#endif
11750 entry->cmdtype = CMDUNKNOWN;
11751 return;
11752 }
11753 }
11754 entry->cmdtype = CMDNORMAL;
11755 return;
11756 }
11757
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011758/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011759
11760 updatetbl = (path == pathval());
11761 if (!updatetbl) {
11762 act |= DO_ALTPATH;
11763 if (strstr(path, "%builtin") != NULL)
11764 act |= DO_ALTBLTIN;
11765 }
11766
11767 /* If name is in the table, check answer will be ok */
11768 cmdp = cmdlookup(name, 0);
11769 if (cmdp != NULL) {
11770 int bit;
11771
11772 switch (cmdp->cmdtype) {
11773 default:
11774#if DEBUG
11775 abort();
11776#endif
11777 case CMDNORMAL:
11778 bit = DO_ALTPATH;
11779 break;
11780 case CMDFUNCTION:
11781 bit = DO_NOFUNC;
11782 break;
11783 case CMDBUILTIN:
11784 bit = DO_ALTBLTIN;
11785 break;
11786 }
11787 if (act & bit) {
11788 updatetbl = 0;
11789 cmdp = NULL;
11790 } else if (cmdp->rehash == 0)
11791 /* if not invalidated by cd, we're done */
11792 goto success;
11793 }
11794
11795 /* If %builtin not in path, check for builtin next */
11796 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011797 if (bcmd) {
11798 if (IS_BUILTIN_REGULAR(bcmd))
11799 goto builtin_success;
11800 if (act & DO_ALTPATH) {
11801 if (!(act & DO_ALTBLTIN))
11802 goto builtin_success;
11803 } else if (builtinloc <= 0) {
11804 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011805 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011806 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011807
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011808#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011809 {
11810 int applet_no = find_applet_by_name(name);
11811 if (applet_no >= 0) {
11812 entry->cmdtype = CMDNORMAL;
11813 entry->u.index = -2 - applet_no;
11814 return;
11815 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011816 }
11817#endif
11818
Denis Vlasenkocc571512007-02-23 21:10:35 +000011819 /* We have to search path. */
11820 prev = -1; /* where to start */
11821 if (cmdp && cmdp->rehash) { /* doing a rehash */
11822 if (cmdp->cmdtype == CMDBUILTIN)
11823 prev = builtinloc;
11824 else
11825 prev = cmdp->param.index;
11826 }
11827
11828 e = ENOENT;
11829 idx = -1;
11830 loop:
11831 while ((fullname = padvance(&path, name)) != NULL) {
11832 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011833 /* NB: code below will still use fullname
11834 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011835 idx++;
11836 if (pathopt) {
11837 if (prefix(pathopt, "builtin")) {
11838 if (bcmd)
11839 goto builtin_success;
11840 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011841 }
11842 if ((act & DO_NOFUNC)
11843 || !prefix(pathopt, "func")
11844 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011845 continue;
11846 }
11847 }
11848 /* if rehash, don't redo absolute path names */
11849 if (fullname[0] == '/' && idx <= prev) {
11850 if (idx < prev)
11851 continue;
11852 TRACE(("searchexec \"%s\": no change\n", name));
11853 goto success;
11854 }
11855 while (stat(fullname, &statb) < 0) {
11856#ifdef SYSV
11857 if (errno == EINTR)
11858 continue;
11859#endif
11860 if (errno != ENOENT && errno != ENOTDIR)
11861 e = errno;
11862 goto loop;
11863 }
11864 e = EACCES; /* if we fail, this will be the error */
11865 if (!S_ISREG(statb.st_mode))
11866 continue;
11867 if (pathopt) { /* this is a %func directory */
11868 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011869 /* NB: stalloc will return space pointed by fullname
11870 * (because we don't have any intervening allocations
11871 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011872 readcmdfile(fullname);
11873 cmdp = cmdlookup(name, 0);
11874 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11875 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11876 stunalloc(fullname);
11877 goto success;
11878 }
11879 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11880 if (!updatetbl) {
11881 entry->cmdtype = CMDNORMAL;
11882 entry->u.index = idx;
11883 return;
11884 }
11885 INT_OFF;
11886 cmdp = cmdlookup(name, 1);
11887 cmdp->cmdtype = CMDNORMAL;
11888 cmdp->param.index = idx;
11889 INT_ON;
11890 goto success;
11891 }
11892
11893 /* We failed. If there was an entry for this command, delete it */
11894 if (cmdp && updatetbl)
11895 delete_cmd_entry();
11896 if (act & DO_ERR)
11897 ash_msg("%s: %s", name, errmsg(e, "not found"));
11898 entry->cmdtype = CMDUNKNOWN;
11899 return;
11900
11901 builtin_success:
11902 if (!updatetbl) {
11903 entry->cmdtype = CMDBUILTIN;
11904 entry->u.cmd = bcmd;
11905 return;
11906 }
11907 INT_OFF;
11908 cmdp = cmdlookup(name, 1);
11909 cmdp->cmdtype = CMDBUILTIN;
11910 cmdp->param.cmd = bcmd;
11911 INT_ON;
11912 success:
11913 cmdp->rehash = 0;
11914 entry->cmdtype = cmdp->cmdtype;
11915 entry->u = cmdp->param;
11916}
11917
11918
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011919/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011920
Eric Andersencb57d552001-06-28 07:25:16 +000011921/*
Eric Andersencb57d552001-06-28 07:25:16 +000011922 * The trap builtin.
11923 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011924static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011925trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011926{
11927 char *action;
11928 char **ap;
11929 int signo;
11930
Eric Andersenc470f442003-07-28 09:56:35 +000011931 nextopt(nullstr);
11932 ap = argptr;
11933 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011934 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011935 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011936 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000011937 single_quote(trap[signo]),
11938 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000011939 }
11940 }
11941 return 0;
11942 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000011943 action = NULL;
11944 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011945 action = *ap++;
11946 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011947 signo = get_signum(*ap);
11948 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011949 ash_msg_and_raise_error("%s: bad trap", *ap);
11950 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011951 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011952 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011953 action = NULL;
11954 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011955 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011956 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011957 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011958 trap[signo] = action;
11959 if (signo != 0)
11960 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011961 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011962 ap++;
11963 }
11964 return 0;
11965}
11966
Eric Andersenc470f442003-07-28 09:56:35 +000011967
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011968/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011969
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011970#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011971/*
11972 * Lists available builtins
11973 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011974static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011975helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011976{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011977 unsigned col;
11978 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011979
11980 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011981 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011982 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011983 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011984 if (col > 60) {
11985 out1fmt("\n");
11986 col = 0;
11987 }
11988 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011989#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011990 {
11991 const char *a = applet_names;
11992 while (*a) {
11993 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11994 if (col > 60) {
11995 out1fmt("\n");
11996 col = 0;
11997 }
11998 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011999 }
12000 }
12001#endif
12002 out1fmt("\n\n");
12003 return EXIT_SUCCESS;
12004}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012005#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012006
Eric Andersencb57d552001-06-28 07:25:16 +000012007/*
Eric Andersencb57d552001-06-28 07:25:16 +000012008 * The export and readonly commands.
12009 */
Eric Andersenc470f442003-07-28 09:56:35 +000012010static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012011exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012012{
12013 struct var *vp;
12014 char *name;
12015 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012016 char **aptr;
12017 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012018
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012019 if (nextopt("p") != 'p') {
12020 aptr = argptr;
12021 name = *aptr;
12022 if (name) {
12023 do {
12024 p = strchr(name, '=');
12025 if (p != NULL) {
12026 p++;
12027 } else {
12028 vp = *findvar(hashvar(name), name);
12029 if (vp) {
12030 vp->flags |= flag;
12031 continue;
12032 }
Eric Andersencb57d552001-06-28 07:25:16 +000012033 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012034 setvar(name, p, flag);
12035 } while ((name = *++aptr) != NULL);
12036 return 0;
12037 }
Eric Andersencb57d552001-06-28 07:25:16 +000012038 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012039 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012040 return 0;
12041}
12042
Eric Andersencb57d552001-06-28 07:25:16 +000012043/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012044 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012045 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012046static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012047unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012048{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012049 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012050
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012051 cmdp = cmdlookup(name, 0);
12052 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12053 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012054}
12055
Eric Andersencb57d552001-06-28 07:25:16 +000012056/*
Eric Andersencb57d552001-06-28 07:25:16 +000012057 * The unset builtin command. We unset the function before we unset the
12058 * variable to allow a function to be unset when there is a readonly variable
12059 * with the same name.
12060 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012061static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012062unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012063{
12064 char **ap;
12065 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012066 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012067 int ret = 0;
12068
12069 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012070 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012071 }
Eric Andersencb57d552001-06-28 07:25:16 +000012072
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012073 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012074 if (flag != 'f') {
12075 i = unsetvar(*ap);
12076 ret |= i;
12077 if (!(i & 2))
12078 continue;
12079 }
12080 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012081 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012082 }
Eric Andersenc470f442003-07-28 09:56:35 +000012083 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012084}
12085
12086
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012087/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012088
Eric Andersenc470f442003-07-28 09:56:35 +000012089#include <sys/times.h>
12090
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012091static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012092 ' ', offsetof(struct tms, tms_utime),
12093 '\n', offsetof(struct tms, tms_stime),
12094 ' ', offsetof(struct tms, tms_cutime),
12095 '\n', offsetof(struct tms, tms_cstime),
12096 0
12097};
Eric Andersencb57d552001-06-28 07:25:16 +000012098
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012099static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012100timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012101{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012102 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012103 const unsigned char *p;
12104 struct tms buf;
12105
12106 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012107 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012108
12109 p = timescmd_str;
12110 do {
12111 t = *(clock_t *)(((char *) &buf) + p[1]);
12112 s = t / clk_tck;
12113 out1fmt("%ldm%ld.%.3lds%c",
12114 s/60, s%60,
12115 ((t - s * clk_tck) * 1000) / clk_tck,
12116 p[0]);
12117 } while (*(p += 2));
12118
Eric Andersencb57d552001-06-28 07:25:16 +000012119 return 0;
12120}
12121
Denis Vlasenko131ae172007-02-18 13:00:19 +000012122#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012123static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012124dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012125{
Eric Andersened9ecf72004-06-22 08:29:45 +000012126 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012127 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012128
Denis Vlasenkob012b102007-02-19 22:43:01 +000012129 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012130 result = arith(s, &errcode);
12131 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012132 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012133 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012134 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012135 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012136 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012137 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012138 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012139 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012140 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012141
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012142 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012143}
Eric Andersenc470f442003-07-28 09:56:35 +000012144
Eric Andersenc470f442003-07-28 09:56:35 +000012145/*
Eric Andersen90898442003-08-06 11:20:52 +000012146 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12147 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12148 *
12149 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012150 */
12151static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012152letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012153{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012154 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012155
Denis Vlasenko68404f12008-03-17 09:00:54 +000012156 argv++;
12157 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012158 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012159 do {
12160 i = dash_arith(*argv);
12161 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012162
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012163 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012164}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012165#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012166
Eric Andersenc470f442003-07-28 09:56:35 +000012167
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012168/* ============ miscbltin.c
12169 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012170 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012171 */
12172
12173#undef rflag
12174
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012175#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012176typedef enum __rlimit_resource rlim_t;
12177#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012178
Eric Andersenc470f442003-07-28 09:56:35 +000012179/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012180 * The read builtin. Options:
12181 * -r Do not interpret '\' specially
12182 * -s Turn off echo (tty only)
12183 * -n NCHARS Read NCHARS max
12184 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12185 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12186 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012187 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012188 * TODO: bash also has:
12189 * -a ARRAY Read into array[0],[1],etc
12190 * -d DELIM End on DELIM char, not newline
12191 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012192 */
Eric Andersenc470f442003-07-28 09:56:35 +000012193static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012194readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012195{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012196 static const char *const arg_REPLY[] = { "REPLY", NULL };
12197
Eric Andersenc470f442003-07-28 09:56:35 +000012198 char **ap;
12199 int backslash;
12200 char c;
12201 int rflag;
12202 char *prompt;
12203 const char *ifs;
12204 char *p;
12205 int startword;
12206 int status;
12207 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012208 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012209#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012210 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012211 int silent = 0;
12212 struct termios tty, old_tty;
12213#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012214#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012215 unsigned end_ms = 0;
12216 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012217#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012218
12219 rflag = 0;
12220 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012221 while ((i = nextopt("p:u:r"
12222 USE_ASH_READ_TIMEOUT("t:")
12223 USE_ASH_READ_NCHARS("n:s")
12224 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012225 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012226 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012227 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012228 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012229#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012230 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012231 nchars = bb_strtou(optionarg, NULL, 10);
12232 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012233 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012234 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012235 break;
12236 case 's':
12237 silent = 1;
12238 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012239#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012240#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012241 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012242 timeout = bb_strtou(optionarg, NULL, 10);
12243 if (errno || timeout > UINT_MAX / 2048)
12244 ash_msg_and_raise_error("invalid timeout");
12245 timeout *= 1000;
12246#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012247 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012248 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012249 /* EINVAL means number is ok, but not terminated by NUL */
12250 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012251 char *p2;
12252 if (*++p) {
12253 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012254 ts.tv_usec = bb_strtou(p, &p2, 10);
12255 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012256 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012257 scale = p2 - p;
12258 /* normalize to usec */
12259 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012260 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012261 while (scale++ < 6)
12262 ts.tv_usec *= 10;
12263 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012264 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012265 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012266 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012267 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012268 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012269 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012270#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012271 break;
12272#endif
12273 case 'r':
12274 rflag = 1;
12275 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012276 case 'u':
12277 fd = bb_strtou(optionarg, NULL, 10);
12278 if (fd < 0 || errno)
12279 ash_msg_and_raise_error("invalid file descriptor");
12280 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012281 default:
12282 break;
12283 }
Eric Andersenc470f442003-07-28 09:56:35 +000012284 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012285 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012286 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012287 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012288 ap = argptr;
12289 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012290 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012291 ifs = bltinlookup("IFS");
12292 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012293 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012294#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012295 tcgetattr(fd, &tty);
12296 old_tty = tty;
12297 if (nchars || silent) {
12298 if (nchars) {
12299 tty.c_lflag &= ~ICANON;
12300 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012301 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012302 if (silent) {
12303 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12304 }
12305 /* if tcgetattr failed, tcsetattr will fail too.
12306 * Ignoring, it's harmless. */
12307 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012308 }
12309#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012310
Eric Andersenc470f442003-07-28 09:56:35 +000012311 status = 0;
12312 startword = 1;
12313 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012314#if ENABLE_ASH_READ_TIMEOUT
12315 if (timeout) /* NB: ensuring end_ms is nonzero */
12316 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12317#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012318 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012319 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012320#if ENABLE_ASH_READ_TIMEOUT
12321 if (end_ms) {
12322 struct pollfd pfd[1];
12323 pfd[0].fd = fd;
12324 pfd[0].events = POLLIN;
12325 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12326 if ((int)timeout <= 0 /* already late? */
12327 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12328 ) { /* timed out! */
12329#if ENABLE_ASH_READ_NCHARS
12330 tcsetattr(fd, TCSANOW, &old_tty);
12331#endif
12332 return 1;
12333 }
12334 }
12335#endif
12336 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012337 status = 1;
12338 break;
12339 }
12340 if (c == '\0')
12341 continue;
12342 if (backslash) {
12343 backslash = 0;
12344 if (c != '\n')
12345 goto put;
12346 continue;
12347 }
12348 if (!rflag && c == '\\') {
12349 backslash++;
12350 continue;
12351 }
12352 if (c == '\n')
12353 break;
12354 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12355 continue;
12356 }
12357 startword = 0;
12358 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12359 STACKSTRNUL(p);
12360 setvar(*ap, stackblock(), 0);
12361 ap++;
12362 startword = 1;
12363 STARTSTACKSTR(p);
12364 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012365 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012366 STPUTC(c, p);
12367 }
12368 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012369/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012370#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012371 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012372#else
12373 while (1);
12374#endif
12375
12376#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012377 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012378#endif
12379
Eric Andersenc470f442003-07-28 09:56:35 +000012380 STACKSTRNUL(p);
12381 /* Remove trailing blanks */
12382 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12383 *p = '\0';
12384 setvar(*ap, stackblock(), 0);
12385 while (*++ap != NULL)
12386 setvar(*ap, nullstr, 0);
12387 return status;
12388}
12389
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012390static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012391umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012392{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012393 static const char permuser[3] ALIGN1 = "ugo";
12394 static const char permmode[3] ALIGN1 = "rwx";
12395 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012396 S_IRUSR, S_IWUSR, S_IXUSR,
12397 S_IRGRP, S_IWGRP, S_IXGRP,
12398 S_IROTH, S_IWOTH, S_IXOTH
12399 };
12400
12401 char *ap;
12402 mode_t mask;
12403 int i;
12404 int symbolic_mode = 0;
12405
12406 while (nextopt("S") != '\0') {
12407 symbolic_mode = 1;
12408 }
12409
Denis Vlasenkob012b102007-02-19 22:43:01 +000012410 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012411 mask = umask(0);
12412 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012413 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012414
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012415 ap = *argptr;
12416 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012417 if (symbolic_mode) {
12418 char buf[18];
12419 char *p = buf;
12420
12421 for (i = 0; i < 3; i++) {
12422 int j;
12423
12424 *p++ = permuser[i];
12425 *p++ = '=';
12426 for (j = 0; j < 3; j++) {
12427 if ((mask & permmask[3 * i + j]) == 0) {
12428 *p++ = permmode[j];
12429 }
12430 }
12431 *p++ = ',';
12432 }
12433 *--p = 0;
12434 puts(buf);
12435 } else {
12436 out1fmt("%.4o\n", mask);
12437 }
12438 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012439 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012440 mask = 0;
12441 do {
12442 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012443 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012444 mask = (mask << 3) + (*ap - '0');
12445 } while (*++ap != '\0');
12446 umask(mask);
12447 } else {
12448 mask = ~mask & 0777;
12449 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012450 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012451 }
12452 umask(~mask & 0777);
12453 }
12454 }
12455 return 0;
12456}
12457
12458/*
12459 * ulimit builtin
12460 *
12461 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12462 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12463 * ash by J.T. Conklin.
12464 *
12465 * Public domain.
12466 */
12467
12468struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012469 uint8_t cmd; /* RLIMIT_xxx fit into it */
12470 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012471 char option;
12472};
12473
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012474static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012475#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012476 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012477#endif
12478#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012479 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012480#endif
12481#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012482 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012483#endif
12484#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012485 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012486#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012487#ifdef RLIMIT_CORE
12488 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012489#endif
12490#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012491 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012492#endif
12493#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012494 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012495#endif
12496#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012497 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012498#endif
12499#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012500 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012501#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012502#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012503 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012504#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012505#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012506 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012507#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012508};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012509static const char limits_name[] =
12510#ifdef RLIMIT_CPU
12511 "time(seconds)" "\0"
12512#endif
12513#ifdef RLIMIT_FSIZE
12514 "file(blocks)" "\0"
12515#endif
12516#ifdef RLIMIT_DATA
12517 "data(kb)" "\0"
12518#endif
12519#ifdef RLIMIT_STACK
12520 "stack(kb)" "\0"
12521#endif
12522#ifdef RLIMIT_CORE
12523 "coredump(blocks)" "\0"
12524#endif
12525#ifdef RLIMIT_RSS
12526 "memory(kb)" "\0"
12527#endif
12528#ifdef RLIMIT_MEMLOCK
12529 "locked memory(kb)" "\0"
12530#endif
12531#ifdef RLIMIT_NPROC
12532 "process" "\0"
12533#endif
12534#ifdef RLIMIT_NOFILE
12535 "nofiles" "\0"
12536#endif
12537#ifdef RLIMIT_AS
12538 "vmemory(kb)" "\0"
12539#endif
12540#ifdef RLIMIT_LOCKS
12541 "locks" "\0"
12542#endif
12543;
Eric Andersenc470f442003-07-28 09:56:35 +000012544
Glenn L McGrath76620622004-01-13 10:19:37 +000012545enum limtype { SOFT = 0x1, HARD = 0x2 };
12546
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012547static void
12548printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012549 const struct limits *l)
12550{
12551 rlim_t val;
12552
12553 val = limit->rlim_max;
12554 if (how & SOFT)
12555 val = limit->rlim_cur;
12556
12557 if (val == RLIM_INFINITY)
12558 out1fmt("unlimited\n");
12559 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012560 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012561 out1fmt("%lld\n", (long long) val);
12562 }
12563}
12564
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012565static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012566ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012567{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012568 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012569 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012570 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012571 const struct limits *l;
12572 int set, all = 0;
12573 int optc, what;
12574 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012575
12576 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012577 while ((optc = nextopt("HSa"
12578#ifdef RLIMIT_CPU
12579 "t"
12580#endif
12581#ifdef RLIMIT_FSIZE
12582 "f"
12583#endif
12584#ifdef RLIMIT_DATA
12585 "d"
12586#endif
12587#ifdef RLIMIT_STACK
12588 "s"
12589#endif
12590#ifdef RLIMIT_CORE
12591 "c"
12592#endif
12593#ifdef RLIMIT_RSS
12594 "m"
12595#endif
12596#ifdef RLIMIT_MEMLOCK
12597 "l"
12598#endif
12599#ifdef RLIMIT_NPROC
12600 "p"
12601#endif
12602#ifdef RLIMIT_NOFILE
12603 "n"
12604#endif
12605#ifdef RLIMIT_AS
12606 "v"
12607#endif
12608#ifdef RLIMIT_LOCKS
12609 "w"
12610#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012611 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012612 switch (optc) {
12613 case 'H':
12614 how = HARD;
12615 break;
12616 case 'S':
12617 how = SOFT;
12618 break;
12619 case 'a':
12620 all = 1;
12621 break;
12622 default:
12623 what = optc;
12624 }
12625
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012626 for (l = limits_tbl; l->option != what; l++)
12627 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012628
12629 set = *argptr ? 1 : 0;
12630 if (set) {
12631 char *p = *argptr;
12632
12633 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012634 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012635 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012636 val = RLIM_INFINITY;
12637 else {
12638 val = (rlim_t) 0;
12639
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012640 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012641 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012642 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012643 if (val < (rlim_t) 0)
12644 break;
12645 }
12646 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012647 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012648 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012649 }
12650 }
12651 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012652 const char *lname = limits_name;
12653 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012654 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012655 out1fmt("%-20s ", lname);
12656 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012657 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012658 }
12659 return 0;
12660 }
12661
12662 getrlimit(l->cmd, &limit);
12663 if (set) {
12664 if (how & HARD)
12665 limit.rlim_max = val;
12666 if (how & SOFT)
12667 limit.rlim_cur = val;
12668 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012669 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012670 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012671 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012672 }
12673 return 0;
12674}
12675
Eric Andersen90898442003-08-06 11:20:52 +000012676
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012677/* ============ Math support */
12678
Denis Vlasenko131ae172007-02-18 13:00:19 +000012679#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012680
12681/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12682
12683 Permission is hereby granted, free of charge, to any person obtaining
12684 a copy of this software and associated documentation files (the
12685 "Software"), to deal in the Software without restriction, including
12686 without limitation the rights to use, copy, modify, merge, publish,
12687 distribute, sublicense, and/or sell copies of the Software, and to
12688 permit persons to whom the Software is furnished to do so, subject to
12689 the following conditions:
12690
12691 The above copyright notice and this permission notice shall be
12692 included in all copies or substantial portions of the Software.
12693
12694 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12695 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12696 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12697 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12698 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12699 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12700 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12701*/
12702
12703/* This is my infix parser/evaluator. It is optimized for size, intended
12704 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012705 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012706 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012707 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012708 * be that which POSIX specifies for shells. */
12709
12710/* The code uses a simple two-stack algorithm. See
12711 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012712 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012713 * this is based (this code differs in that it applies operators immediately
12714 * to the stack instead of adding them to a queue to end up with an
12715 * expression). */
12716
12717/* To use the routine, call it with an expression string and error return
12718 * pointer */
12719
12720/*
12721 * Aug 24, 2001 Manuel Novoa III
12722 *
12723 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12724 *
12725 * 1) In arith_apply():
12726 * a) Cached values of *numptr and &(numptr[-1]).
12727 * b) Removed redundant test for zero denominator.
12728 *
12729 * 2) In arith():
12730 * a) Eliminated redundant code for processing operator tokens by moving
12731 * to a table-based implementation. Also folded handling of parens
12732 * into the table.
12733 * b) Combined all 3 loops which called arith_apply to reduce generated
12734 * code size at the cost of speed.
12735 *
12736 * 3) The following expressions were treated as valid by the original code:
12737 * 1() , 0! , 1 ( *3 ) .
12738 * These bugs have been fixed by internally enclosing the expression in
12739 * parens and then checking that all binary ops and right parens are
12740 * preceded by a valid expression (NUM_TOKEN).
12741 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012742 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012743 * ctype's isspace() if it is used by another busybox applet or if additional
12744 * whitespace chars should be considered. Look below the "#include"s for a
12745 * precompiler test.
12746 */
12747
12748/*
12749 * Aug 26, 2001 Manuel Novoa III
12750 *
12751 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12752 *
12753 * Merge in Aaron's comments previously posted to the busybox list,
12754 * modified slightly to take account of my changes to the code.
12755 *
12756 */
12757
12758/*
12759 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12760 *
12761 * - allow access to variable,
12762 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12763 * - realize assign syntax (VAR=expr, +=, *= etc)
12764 * - realize exponentiation (** operator)
12765 * - realize comma separated - expr, expr
12766 * - realise ++expr --expr expr++ expr--
12767 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012768 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012769 * - was restored loses XOR operator
12770 * - remove one goto label, added three ;-)
12771 * - protect $((num num)) as true zero expr (Manuel`s error)
12772 * - always use special isspace(), see comment from bash ;-)
12773 */
12774
Eric Andersen90898442003-08-06 11:20:52 +000012775#define arith_isspace(arithval) \
12776 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12777
Eric Andersen90898442003-08-06 11:20:52 +000012778typedef unsigned char operator;
12779
12780/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012781 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012782 * precedence. The ID portion is so that multiple operators can have the
12783 * same precedence, ensuring that the leftmost one is evaluated first.
12784 * Consider * and /. */
12785
12786#define tok_decl(prec,id) (((id)<<5)|(prec))
12787#define PREC(op) ((op) & 0x1F)
12788
12789#define TOK_LPAREN tok_decl(0,0)
12790
12791#define TOK_COMMA tok_decl(1,0)
12792
12793#define TOK_ASSIGN tok_decl(2,0)
12794#define TOK_AND_ASSIGN tok_decl(2,1)
12795#define TOK_OR_ASSIGN tok_decl(2,2)
12796#define TOK_XOR_ASSIGN tok_decl(2,3)
12797#define TOK_PLUS_ASSIGN tok_decl(2,4)
12798#define TOK_MINUS_ASSIGN tok_decl(2,5)
12799#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12800#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12801
12802#define TOK_MUL_ASSIGN tok_decl(3,0)
12803#define TOK_DIV_ASSIGN tok_decl(3,1)
12804#define TOK_REM_ASSIGN tok_decl(3,2)
12805
12806/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012807#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012808
12809/* conditional is right associativity too */
12810#define TOK_CONDITIONAL tok_decl(4,0)
12811#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12812
12813#define TOK_OR tok_decl(5,0)
12814
12815#define TOK_AND tok_decl(6,0)
12816
12817#define TOK_BOR tok_decl(7,0)
12818
12819#define TOK_BXOR tok_decl(8,0)
12820
12821#define TOK_BAND tok_decl(9,0)
12822
12823#define TOK_EQ tok_decl(10,0)
12824#define TOK_NE tok_decl(10,1)
12825
12826#define TOK_LT tok_decl(11,0)
12827#define TOK_GT tok_decl(11,1)
12828#define TOK_GE tok_decl(11,2)
12829#define TOK_LE tok_decl(11,3)
12830
12831#define TOK_LSHIFT tok_decl(12,0)
12832#define TOK_RSHIFT tok_decl(12,1)
12833
12834#define TOK_ADD tok_decl(13,0)
12835#define TOK_SUB tok_decl(13,1)
12836
12837#define TOK_MUL tok_decl(14,0)
12838#define TOK_DIV tok_decl(14,1)
12839#define TOK_REM tok_decl(14,2)
12840
12841/* exponent is right associativity */
12842#define TOK_EXPONENT tok_decl(15,1)
12843
12844/* For now unary operators. */
12845#define UNARYPREC 16
12846#define TOK_BNOT tok_decl(UNARYPREC,0)
12847#define TOK_NOT tok_decl(UNARYPREC,1)
12848
12849#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12850#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12851
12852#define PREC_PRE (UNARYPREC+2)
12853
12854#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12855#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12856
12857#define PREC_POST (UNARYPREC+3)
12858
12859#define TOK_POST_INC tok_decl(PREC_POST, 0)
12860#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12861
12862#define SPEC_PREC (UNARYPREC+4)
12863
12864#define TOK_NUM tok_decl(SPEC_PREC, 0)
12865#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12866
12867#define NUMPTR (*numstackptr)
12868
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012869static int
12870tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012871{
12872 operator prec = PREC(op);
12873
12874 convert_prec_is_assing(prec);
12875 return (prec == PREC(TOK_ASSIGN) ||
12876 prec == PREC_PRE || prec == PREC_POST);
12877}
12878
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012879static int
12880is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012881{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012882 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12883 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012884}
12885
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012886typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012887 arith_t val;
12888 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012889 char contidional_second_val_initialized;
12890 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012891 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012892} v_n_t;
12893
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012894typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012895 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012896 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012897} chk_var_recursive_looped_t;
12898
12899static chk_var_recursive_looped_t *prev_chk_var_recursive;
12900
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012901static int
12902arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012903{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012904 if (t->var) {
12905 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012906
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012907 if (p) {
12908 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012909
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012910 /* recursive try as expression */
12911 chk_var_recursive_looped_t *cur;
12912 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012913
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012914 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12915 if (strcmp(cur->var, t->var) == 0) {
12916 /* expression recursion loop detected */
12917 return -5;
12918 }
12919 }
12920 /* save current lookuped var name */
12921 cur = prev_chk_var_recursive;
12922 cur_save.var = t->var;
12923 cur_save.next = cur;
12924 prev_chk_var_recursive = &cur_save;
12925
12926 t->val = arith (p, &errcode);
12927 /* restore previous ptr after recursiving */
12928 prev_chk_var_recursive = cur;
12929 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012930 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012931 /* allow undefined var as 0 */
12932 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012933 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012934 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012935}
12936
12937/* "applying" a token means performing it on the top elements on the integer
12938 * stack. For a unary operator it will only change the top element, but a
12939 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012940static int
12941arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012942{
Eric Andersen90898442003-08-06 11:20:52 +000012943 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012944 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012945 int ret_arith_lookup_val;
12946
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012947 /* There is no operator that can work without arguments */
12948 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012949 numptr_m1 = NUMPTR - 1;
12950
12951 /* check operand is var with noninteger value */
12952 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012953 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012954 return ret_arith_lookup_val;
12955
12956 rez = numptr_m1->val;
12957 if (op == TOK_UMINUS)
12958 rez *= -1;
12959 else if (op == TOK_NOT)
12960 rez = !rez;
12961 else if (op == TOK_BNOT)
12962 rez = ~rez;
12963 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12964 rez++;
12965 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12966 rez--;
12967 else if (op != TOK_UPLUS) {
12968 /* Binary operators */
12969
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012970 /* check and binary operators need two arguments */
12971 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012972
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012973 /* ... and they pop one */
12974 --NUMPTR;
12975 numptr_val = rez;
12976 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000012977 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012978 /* protect $((expr1 ? expr2)) without ": expr" */
12979 goto err;
12980 }
12981 rez = numptr_m1->contidional_second_val;
12982 } else if (numptr_m1->contidional_second_val_initialized) {
12983 /* protect $((expr1 : expr2)) without "expr ? " */
12984 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012985 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012986 numptr_m1 = NUMPTR - 1;
12987 if (op != TOK_ASSIGN) {
12988 /* check operand is var with noninteger value for not '=' */
12989 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12990 if (ret_arith_lookup_val)
12991 return ret_arith_lookup_val;
12992 }
12993 if (op == TOK_CONDITIONAL) {
12994 numptr_m1->contidional_second_val = rez;
12995 }
12996 rez = numptr_m1->val;
12997 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012998 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012999 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013000 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013001 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013002 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013003 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013004 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013005 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013006 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013007 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013008 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013009 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013010 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013011 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013012 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013013 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013014 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013015 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013016 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013018 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013019 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013020 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013021 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013022 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013023 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013024 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013025 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013026 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013027 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013028 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013029 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013030 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013031 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013032 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013033 /* protect $((expr : expr)) without "expr ? " */
13034 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013035 }
13036 numptr_m1->contidional_second_val_initialized = op;
13037 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013038 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013039 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013040 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013041 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013042 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013043 return -3; /* exponent less than 0 */
13044 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013045 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013046
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013047 if (numptr_val)
13048 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013049 c *= rez;
13050 rez = c;
13051 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013052 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013053 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013054 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013055 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013056 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013057 rez %= numptr_val;
13058 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013059 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013060 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013061
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013062 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013063 /* Hmm, 1=2 ? */
13064 goto err;
13065 }
13066 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013067#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013068 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013069#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013070 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013071#endif
Eric Andersen90898442003-08-06 11:20:52 +000013072 setvar(numptr_m1->var, buf, 0);
13073 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013074 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013075 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013076 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013077 rez++;
13078 }
13079 numptr_m1->val = rez;
13080 /* protect geting var value, is number now */
13081 numptr_m1->var = NULL;
13082 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013083 err:
13084 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013085}
13086
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013087/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013088static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013089 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13090 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13091 '<','<', 0, TOK_LSHIFT,
13092 '>','>', 0, TOK_RSHIFT,
13093 '|','|', 0, TOK_OR,
13094 '&','&', 0, TOK_AND,
13095 '!','=', 0, TOK_NE,
13096 '<','=', 0, TOK_LE,
13097 '>','=', 0, TOK_GE,
13098 '=','=', 0, TOK_EQ,
13099 '|','=', 0, TOK_OR_ASSIGN,
13100 '&','=', 0, TOK_AND_ASSIGN,
13101 '*','=', 0, TOK_MUL_ASSIGN,
13102 '/','=', 0, TOK_DIV_ASSIGN,
13103 '%','=', 0, TOK_REM_ASSIGN,
13104 '+','=', 0, TOK_PLUS_ASSIGN,
13105 '-','=', 0, TOK_MINUS_ASSIGN,
13106 '-','-', 0, TOK_POST_DEC,
13107 '^','=', 0, TOK_XOR_ASSIGN,
13108 '+','+', 0, TOK_POST_INC,
13109 '*','*', 0, TOK_EXPONENT,
13110 '!', 0, TOK_NOT,
13111 '<', 0, TOK_LT,
13112 '>', 0, TOK_GT,
13113 '=', 0, TOK_ASSIGN,
13114 '|', 0, TOK_BOR,
13115 '&', 0, TOK_BAND,
13116 '*', 0, TOK_MUL,
13117 '/', 0, TOK_DIV,
13118 '%', 0, TOK_REM,
13119 '+', 0, TOK_ADD,
13120 '-', 0, TOK_SUB,
13121 '^', 0, TOK_BXOR,
13122 /* uniq */
13123 '~', 0, TOK_BNOT,
13124 ',', 0, TOK_COMMA,
13125 '?', 0, TOK_CONDITIONAL,
13126 ':', 0, TOK_CONDITIONAL_SEP,
13127 ')', 0, TOK_RPAREN,
13128 '(', 0, TOK_LPAREN,
13129 0
13130};
13131/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013132#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013133
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013134static arith_t
13135arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013136{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013137 char arithval; /* Current character under analysis */
13138 operator lasttok, op;
13139 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013140 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013141 const char *p = endexpression;
13142 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013143 v_n_t *numstack, *numstackptr;
13144 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013145
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013146 /* Stack of integers */
13147 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13148 * in any given correct or incorrect expression is left as an exercise to
13149 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013150 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013151 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013152 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013153
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013154 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13155 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013156
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013157 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013158 arithval = *expr;
13159 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013160 if (p == endexpression) {
13161 /* Null expression. */
13162 return 0;
13163 }
13164
13165 /* This is only reached after all tokens have been extracted from the
13166 * input stream. If there are still tokens on the operator stack, they
13167 * are to be applied in order. At the end, there should be a final
13168 * result on the integer stack */
13169
13170 if (expr != endexpression + 1) {
13171 /* If we haven't done so already, */
13172 /* append a closing right paren */
13173 expr = endexpression;
13174 /* and let the loop process it. */
13175 continue;
13176 }
13177 /* At this point, we're done with the expression. */
13178 if (numstackptr != numstack+1) {
13179 /* ... but if there isn't, it's bad */
13180 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013181 *perrcode = -1;
13182 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013183 }
13184 if (numstack->var) {
13185 /* expression is $((var)) only, lookup now */
13186 errcode = arith_lookup_val(numstack);
13187 }
13188 ret:
13189 *perrcode = errcode;
13190 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013191 }
13192
Eric Andersen90898442003-08-06 11:20:52 +000013193 /* Continue processing the expression. */
13194 if (arith_isspace(arithval)) {
13195 /* Skip whitespace */
13196 goto prologue;
13197 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013198 p = endofname(expr);
13199 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013200 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013201
13202 numstackptr->var = alloca(var_name_size);
13203 safe_strncpy(numstackptr->var, expr, var_name_size);
13204 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013205 num:
Eric Andersen90898442003-08-06 11:20:52 +000013206 numstackptr->contidional_second_val_initialized = 0;
13207 numstackptr++;
13208 lasttok = TOK_NUM;
13209 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013210 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013211 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013212 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013213#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013214 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013215#else
13216 numstackptr->val = strtol(expr, (char **) &expr, 0);
13217#endif
Eric Andersen90898442003-08-06 11:20:52 +000013218 goto num;
13219 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013220 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013221 const char *o;
13222
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013223 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013224 /* strange operator not found */
13225 goto err;
13226 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013227 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013228 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013229 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013230 /* found */
13231 expr = o - 1;
13232 break;
13233 }
13234 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013235 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013236 p++;
13237 /* skip zero delim */
13238 p++;
13239 }
13240 op = p[1];
13241
13242 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013243 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13244 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013245
13246 /* Plus and minus are binary (not unary) _only_ if the last
13247 * token was as number, or a right paren (which pretends to be
13248 * a number, since it evaluates to one). Think about it.
13249 * It makes sense. */
13250 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013251 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013252 case TOK_ADD:
13253 op = TOK_UPLUS;
13254 break;
13255 case TOK_SUB:
13256 op = TOK_UMINUS;
13257 break;
13258 case TOK_POST_INC:
13259 op = TOK_PRE_INC;
13260 break;
13261 case TOK_POST_DEC:
13262 op = TOK_PRE_DEC;
13263 break;
Eric Andersen90898442003-08-06 11:20:52 +000013264 }
13265 }
13266 /* We don't want a unary operator to cause recursive descent on the
13267 * stack, because there can be many in a row and it could cause an
13268 * operator to be evaluated before its argument is pushed onto the
13269 * integer stack. */
13270 /* But for binary operators, "apply" everything on the operator
13271 * stack until we find an operator with a lesser priority than the
13272 * one we have just extracted. */
13273 /* Left paren is given the lowest priority so it will never be
13274 * "applied" in this way.
13275 * if associativity is right and priority eq, applied also skip
13276 */
13277 prec = PREC(op);
13278 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13279 /* not left paren or unary */
13280 if (lasttok != TOK_NUM) {
13281 /* binary op must be preceded by a num */
13282 goto err;
13283 }
13284 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013285 if (op == TOK_RPAREN) {
13286 /* The algorithm employed here is simple: while we don't
13287 * hit an open paren nor the bottom of the stack, pop
13288 * tokens and apply them */
13289 if (stackptr[-1] == TOK_LPAREN) {
13290 --stackptr;
13291 /* Any operator directly after a */
13292 lasttok = TOK_NUM;
13293 /* close paren should consider itself binary */
13294 goto prologue;
13295 }
13296 } else {
13297 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013298
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013299 convert_prec_is_assing(prec);
13300 convert_prec_is_assing(prev_prec);
13301 if (prev_prec < prec)
13302 break;
13303 /* check right assoc */
13304 if (prev_prec == prec && is_right_associativity(prec))
13305 break;
13306 }
13307 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13308 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013309 }
13310 if (op == TOK_RPAREN) {
13311 goto err;
13312 }
13313 }
13314
13315 /* Push this operator to the stack and remember it. */
13316 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013317 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013318 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013319 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013320}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013321#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013322
13323
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013324/* ============ main() and helpers */
13325
13326/*
13327 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013328 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013329static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013330static void
13331exitshell(void)
13332{
13333 struct jmploc loc;
13334 char *p;
13335 int status;
13336
13337 status = exitstatus;
13338 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13339 if (setjmp(loc.loc)) {
13340 if (exception == EXEXIT)
13341/* dash bug: it just does _exit(exitstatus) here
13342 * but we have to do setjobctl(0) first!
13343 * (bug is still not fixed in dash-0.5.3 - if you run dash
13344 * under Midnight Commander, on exit from dash MC is backgrounded) */
13345 status = exitstatus;
13346 goto out;
13347 }
13348 exception_handler = &loc;
13349 p = trap[0];
13350 if (p) {
13351 trap[0] = NULL;
13352 evalstring(p, 0);
13353 }
13354 flush_stdout_stderr();
13355 out:
13356 setjobctl(0);
13357 _exit(status);
13358 /* NOTREACHED */
13359}
13360
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013361static void
13362init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013363{
13364 /* from input.c: */
13365 basepf.nextc = basepf.buf = basebuf;
13366
13367 /* from trap.c: */
13368 signal(SIGCHLD, SIG_DFL);
13369
13370 /* from var.c: */
13371 {
13372 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013373 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013374 const char *p;
13375 struct stat st1, st2;
13376
13377 initvar();
13378 for (envp = environ; envp && *envp; envp++) {
13379 if (strchr(*envp, '=')) {
13380 setvareq(*envp, VEXPORT|VTEXTFIXED);
13381 }
13382 }
13383
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013384 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013385 setvar("PPID", ppid, 0);
13386
13387 p = lookupvar("PWD");
13388 if (p)
13389 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13390 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13391 p = '\0';
13392 setpwd(p, 0);
13393 }
13394}
13395
13396/*
13397 * Process the shell command line arguments.
13398 */
13399static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013400procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013401{
13402 int i;
13403 const char *xminusc;
13404 char **xargv;
13405
13406 xargv = argv;
13407 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013408 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013409 xargv++;
13410 for (i = 0; i < NOPTS; i++)
13411 optlist[i] = 2;
13412 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013413 if (options(1)) {
13414 /* it already printed err message */
13415 raise_exception(EXERROR);
13416 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013417 xargv = argptr;
13418 xminusc = minusc;
13419 if (*xargv == NULL) {
13420 if (xminusc)
13421 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13422 sflag = 1;
13423 }
13424 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13425 iflag = 1;
13426 if (mflag == 2)
13427 mflag = iflag;
13428 for (i = 0; i < NOPTS; i++)
13429 if (optlist[i] == 2)
13430 optlist[i] = 0;
13431#if DEBUG == 2
13432 debug = 1;
13433#endif
13434 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13435 if (xminusc) {
13436 minusc = *xargv++;
13437 if (*xargv)
13438 goto setarg0;
13439 } else if (!sflag) {
13440 setinputfile(*xargv, 0);
13441 setarg0:
13442 arg0 = *xargv++;
13443 commandname = arg0;
13444 }
13445
13446 shellparam.p = xargv;
13447#if ENABLE_ASH_GETOPTS
13448 shellparam.optind = 1;
13449 shellparam.optoff = -1;
13450#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013451 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013452 while (*xargv) {
13453 shellparam.nparam++;
13454 xargv++;
13455 }
13456 optschanged();
13457}
13458
13459/*
13460 * Read /etc/profile or .profile.
13461 */
13462static void
13463read_profile(const char *name)
13464{
13465 int skip;
13466
13467 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13468 return;
13469 skip = cmdloop(0);
13470 popfile();
13471 if (skip)
13472 exitshell();
13473}
13474
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013475/*
13476 * This routine is called when an error or an interrupt occurs in an
13477 * interactive shell and control is returned to the main command loop.
13478 */
13479static void
13480reset(void)
13481{
13482 /* from eval.c: */
13483 evalskip = 0;
13484 loopnest = 0;
13485 /* from input.c: */
13486 parselleft = parsenleft = 0; /* clear input buffer */
13487 popallfiles();
13488 /* from parser.c: */
13489 tokpushback = 0;
13490 checkkwd = 0;
13491 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013492 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013493}
13494
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013495#if PROFILE
13496static short profile_buf[16384];
13497extern int etext();
13498#endif
13499
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013500/*
13501 * Main routine. We initialize things, parse the arguments, execute
13502 * profiles if we're a login shell, and then call cmdloop to execute
13503 * commands. The setjmp call sets up the location to jump to when an
13504 * exception occurs. When an exception occurs the variable "state"
13505 * is used to figure out how far we had gotten.
13506 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013507int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013508int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013509{
13510 char *shinit;
13511 volatile int state;
13512 struct jmploc jmploc;
13513 struct stackmark smark;
13514
Denis Vlasenko01631112007-12-16 17:20:38 +000013515 /* Initialize global data */
13516 INIT_G_misc();
13517 INIT_G_memstack();
13518 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013519#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013520 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013521#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013522 INIT_G_cmdtable();
13523
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013524#if PROFILE
13525 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13526#endif
13527
13528#if ENABLE_FEATURE_EDITING
13529 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13530#endif
13531 state = 0;
13532 if (setjmp(jmploc.loc)) {
13533 int e;
13534 int s;
13535
13536 reset();
13537
13538 e = exception;
13539 if (e == EXERROR)
13540 exitstatus = 2;
13541 s = state;
13542 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13543 exitshell();
13544
13545 if (e == EXINT) {
13546 outcslow('\n', stderr);
13547 }
13548 popstackmark(&smark);
13549 FORCE_INT_ON; /* enable interrupts */
13550 if (s == 1)
13551 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013552 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013553 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013554 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013555 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013556 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013557 }
13558 exception_handler = &jmploc;
13559#if DEBUG
13560 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013561 trace_puts("Shell args: ");
13562 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013563#endif
13564 rootpid = getpid();
13565
13566#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013567 /* Can use monotonic_ns() for better randomness but for now it is
13568 * not used anywhere else in busybox... so avoid bloat */
13569 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013570#endif
13571 init();
13572 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013573 procargs(argv);
13574
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013575#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13576 if (iflag) {
13577 const char *hp = lookupvar("HISTFILE");
13578
13579 if (hp == NULL) {
13580 hp = lookupvar("HOME");
13581 if (hp != NULL) {
13582 char *defhp = concat_path_file(hp, ".ash_history");
13583 setvar("HISTFILE", defhp, 0);
13584 free(defhp);
13585 }
13586 }
13587 }
13588#endif
13589 if (argv[0] && argv[0][0] == '-')
13590 isloginsh = 1;
13591 if (isloginsh) {
13592 state = 1;
13593 read_profile("/etc/profile");
13594 state1:
13595 state = 2;
13596 read_profile(".profile");
13597 }
13598 state2:
13599 state = 3;
13600 if (
13601#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013602 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013603#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013604 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013605 ) {
13606 shinit = lookupvar("ENV");
13607 if (shinit != NULL && *shinit != '\0') {
13608 read_profile(shinit);
13609 }
13610 }
13611 state3:
13612 state = 4;
13613 if (minusc)
13614 evalstring(minusc, 0);
13615
13616 if (sflag || minusc == NULL) {
13617#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013618 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013619 const char *hp = lookupvar("HISTFILE");
13620
13621 if (hp != NULL)
13622 line_input_state->hist_file = hp;
13623 }
13624#endif
13625 state4: /* XXX ??? - why isn't this before the "if" statement */
13626 cmdloop(1);
13627 }
13628#if PROFILE
13629 monitor(0);
13630#endif
13631#ifdef GPROF
13632 {
13633 extern void _mcleanup(void);
13634 _mcleanup();
13635 }
13636#endif
13637 exitshell();
13638 /* NOTREACHED */
13639}
13640
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013641#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013642const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013643int main(int argc, char **argv)
13644{
13645 return ash_main(argc, argv);
13646}
13647#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013648
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013649
Eric Andersendf82f612001-06-28 07:46:40 +000013650/*-
13651 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013652 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013653 *
13654 * This code is derived from software contributed to Berkeley by
13655 * Kenneth Almquist.
13656 *
13657 * Redistribution and use in source and binary forms, with or without
13658 * modification, are permitted provided that the following conditions
13659 * are met:
13660 * 1. Redistributions of source code must retain the above copyright
13661 * notice, this list of conditions and the following disclaimer.
13662 * 2. Redistributions in binary form must reproduce the above copyright
13663 * notice, this list of conditions and the following disclaimer in the
13664 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013665 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013666 * may be used to endorse or promote products derived from this software
13667 * without specific prior written permission.
13668 *
13669 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13670 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13671 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13672 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13673 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13674 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13675 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13676 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13677 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13678 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13679 * SUCH DAMAGE.
13680 */