blob: 1eb29eb9551eb6b8772be845c0a88f03ac5f6fb9 [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;
4908 struct parsefile *pf = g_parsefile;
4909 while (pf) {
4910 if (fd == pf->fd) {
4911 return 1;
4912 }
4913 pf = pf->prev;
4914 }
4915 if (!rp)
4916 return 0;
4917 fd |= COPYFD_RESTORE;
4918 for (i = 0; i < rp->pair_count; i++) {
4919 if (rp->two_fd[i].copy == fd) {
4920 return 1;
4921 }
4922 }
4923 return 0;
4924}
4925
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004926/*
4927 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4928 * old file descriptors are stashed away so that the redirection can be
4929 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4930 * standard output, and the standard error if it becomes a duplicate of
4931 * stdout, is saved in memory.
4932 */
4933/* flags passed to redirect */
4934#define REDIR_PUSH 01 /* save previous values of file descriptors */
4935#define REDIR_SAVEFD2 03 /* set preverrout */
4936static void
4937redirect(union node *redir, int flags)
4938{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004939 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004940 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004941 int i;
4942 int fd;
4943 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004944 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004945
Denis Vlasenko01631112007-12-16 17:20:38 +00004946 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004947 if (!redir) {
4948 return;
4949 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004950
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004951 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004952 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004953 INT_OFF;
4954 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004955 union node *tmp = redir;
4956 do {
4957 sv_pos++;
4958 tmp = tmp->nfile.next;
4959 } while (tmp);
4960 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004961 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004962 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004963 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004964 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004965 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004966 while (sv_pos > 0) {
4967 sv_pos--;
4968 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
4969 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004970 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004971
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004972 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00004973 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004974 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004975 int right_fd = redir->ndup.dupfd;
4976 /* redirect from/to same file descriptor? */
4977 if (right_fd == fd)
4978 continue;
4979 /* echo >&10 and 10 is a fd opened to the sh script? */
4980 if (is_hidden_fd(sv, right_fd)) {
4981 errno = EBADF; /* as if it is closed */
4982 ash_msg_and_raise_error("%d: %m", right_fd);
4983 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00004984 newfd = -1;
4985 } else {
4986 newfd = openredirect(redir); /* always >= 0 */
4987 if (fd == newfd) {
4988 /* Descriptor wasn't open before redirect.
4989 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004990 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00004991 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004992 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00004993 continue;
4994 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004995 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004996 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004997 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004998 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00004999/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5000 * are closed in popredir() in the child, preventing them from leaking
5001 * into child. (popredir() also cleans up the mess in case of failures)
5002 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005003 if (i == -1) {
5004 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005005 if (i != EBADF) {
5006 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005007 if (newfd >= 0)
5008 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005009 errno = i;
5010 ash_msg_and_raise_error("%d: %m", fd);
5011 /* NOTREACHED */
5012 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005013 /* EBADF: it is not open - good, remember to close it */
5014 remember_to_close:
5015 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005016 } else { /* fd is open, save its copy */
5017 /* "exec fd>&-" should not close fds
5018 * which point to script file(s).
5019 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005020 if (is_hidden_fd(sv, fd))
5021 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005022 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005023 if (fd == 2)
5024 copied_fd2 = i;
5025 sv->two_fd[sv_pos].orig = fd;
5026 sv->two_fd[sv_pos].copy = i;
5027 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005028 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005029 if (newfd < 0) {
5030 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005031 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005032 close(fd);
5033 } else {
5034 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005035 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005036 } else if (fd != newfd) { /* move newfd to fd */
5037 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005038 close(newfd);
5039 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005040 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005041
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005042 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005043 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5044 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045}
5046
5047/*
5048 * Undo the effects of the last redirection.
5049 */
5050static void
5051popredir(int drop)
5052{
5053 struct redirtab *rp;
5054 int i;
5055
Denis Vlasenko01631112007-12-16 17:20:38 +00005056 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005057 return;
5058 INT_OFF;
5059 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005060 for (i = 0; i < rp->pair_count; i++) {
5061 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005062 int copy = rp->two_fd[i].copy;
5063 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005064 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005065 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005066 continue;
5067 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005068 if (copy != EMPTY) {
5069 if (!drop || (copy & COPYFD_RESTORE)) {
5070 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005071 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005072 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005073 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005074 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005075 }
5076 }
5077 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005078 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005079 free(rp);
5080 INT_ON;
5081}
5082
5083/*
5084 * Undo all redirections. Called on error or interrupt.
5085 */
5086
5087/*
5088 * Discard all saved file descriptors.
5089 */
5090static void
5091clearredir(int drop)
5092{
5093 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005094 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005095 if (!redirlist)
5096 break;
5097 popredir(drop);
5098 }
5099}
5100
5101static int
5102redirectsafe(union node *redir, int flags)
5103{
5104 int err;
5105 volatile int saveint;
5106 struct jmploc *volatile savehandler = exception_handler;
5107 struct jmploc jmploc;
5108
5109 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005110 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5111 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005112 if (!err) {
5113 exception_handler = &jmploc;
5114 redirect(redir, flags);
5115 }
5116 exception_handler = savehandler;
5117 if (err && exception != EXERROR)
5118 longjmp(exception_handler->loc, 1);
5119 RESTORE_INT(saveint);
5120 return err;
5121}
5122
5123
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005124/* ============ Routines to expand arguments to commands
5125 *
5126 * We have to deal with backquotes, shell variables, and file metacharacters.
5127 */
5128
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005129#if ENABLE_ASH_MATH_SUPPORT_64
5130typedef int64_t arith_t;
5131#define arith_t_type long long
5132#else
5133typedef long arith_t;
5134#define arith_t_type long
5135#endif
5136
5137#if ENABLE_ASH_MATH_SUPPORT
5138static arith_t dash_arith(const char *);
5139static arith_t arith(const char *expr, int *perrcode);
5140#endif
5141
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005142/*
5143 * expandarg flags
5144 */
5145#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5146#define EXP_TILDE 0x2 /* do normal tilde expansion */
5147#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5148#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5149#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5150#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5151#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5152#define EXP_WORD 0x80 /* expand word in parameter expansion */
5153#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5154/*
5155 * _rmescape() flags
5156 */
5157#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5158#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5159#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5160#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5161#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5162
5163/*
5164 * Structure specifying which parts of the string should be searched
5165 * for IFS characters.
5166 */
5167struct ifsregion {
5168 struct ifsregion *next; /* next region in list */
5169 int begoff; /* offset of start of region */
5170 int endoff; /* offset of end of region */
5171 int nulonly; /* search for nul bytes only */
5172};
5173
5174struct arglist {
5175 struct strlist *list;
5176 struct strlist **lastp;
5177};
5178
5179/* output of current string */
5180static char *expdest;
5181/* list of back quote expressions */
5182static struct nodelist *argbackq;
5183/* first struct in list of ifs regions */
5184static struct ifsregion ifsfirst;
5185/* last struct in list */
5186static struct ifsregion *ifslastp;
5187/* holds expanded arg list */
5188static struct arglist exparg;
5189
5190/*
5191 * Our own itoa().
5192 */
5193static int
5194cvtnum(arith_t num)
5195{
5196 int len;
5197
5198 expdest = makestrspace(32, expdest);
5199#if ENABLE_ASH_MATH_SUPPORT_64
5200 len = fmtstr(expdest, 32, "%lld", (long long) num);
5201#else
5202 len = fmtstr(expdest, 32, "%ld", num);
5203#endif
5204 STADJUST(len, expdest);
5205 return len;
5206}
5207
5208static size_t
5209esclen(const char *start, const char *p)
5210{
5211 size_t esc = 0;
5212
5213 while (p > start && *--p == CTLESC) {
5214 esc++;
5215 }
5216 return esc;
5217}
5218
5219/*
5220 * Remove any CTLESC characters from a string.
5221 */
5222static char *
5223_rmescapes(char *str, int flag)
5224{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005225 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005226
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005227 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005228 unsigned inquotes;
5229 int notescaped;
5230 int globbing;
5231
5232 p = strpbrk(str, qchars);
5233 if (!p) {
5234 return str;
5235 }
5236 q = p;
5237 r = str;
5238 if (flag & RMESCAPE_ALLOC) {
5239 size_t len = p - str;
5240 size_t fulllen = len + strlen(p) + 1;
5241
5242 if (flag & RMESCAPE_GROW) {
5243 r = makestrspace(fulllen, expdest);
5244 } else if (flag & RMESCAPE_HEAP) {
5245 r = ckmalloc(fulllen);
5246 } else {
5247 r = stalloc(fulllen);
5248 }
5249 q = r;
5250 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005251 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005252 }
5253 }
5254 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5255 globbing = flag & RMESCAPE_GLOB;
5256 notescaped = globbing;
5257 while (*p) {
5258 if (*p == CTLQUOTEMARK) {
5259 inquotes = ~inquotes;
5260 p++;
5261 notescaped = globbing;
5262 continue;
5263 }
5264 if (*p == '\\') {
5265 /* naked back slash */
5266 notescaped = 0;
5267 goto copy;
5268 }
5269 if (*p == CTLESC) {
5270 p++;
5271 if (notescaped && inquotes && *p != '/') {
5272 *q++ = '\\';
5273 }
5274 }
5275 notescaped = globbing;
5276 copy:
5277 *q++ = *p++;
5278 }
5279 *q = '\0';
5280 if (flag & RMESCAPE_GROW) {
5281 expdest = r;
5282 STADJUST(q - r + 1, expdest);
5283 }
5284 return r;
5285}
5286#define rmescapes(p) _rmescapes((p), 0)
5287
5288#define pmatch(a, b) !fnmatch((a), (b), 0)
5289
5290/*
5291 * Prepare a pattern for a expmeta (internal glob(3)) call.
5292 *
5293 * Returns an stalloced string.
5294 */
5295static char *
5296preglob(const char *pattern, int quoted, int flag)
5297{
5298 flag |= RMESCAPE_GLOB;
5299 if (quoted) {
5300 flag |= RMESCAPE_QUOTED;
5301 }
5302 return _rmescapes((char *)pattern, flag);
5303}
5304
5305/*
5306 * Put a string on the stack.
5307 */
5308static void
5309memtodest(const char *p, size_t len, int syntax, int quotes)
5310{
5311 char *q = expdest;
5312
5313 q = makestrspace(len * 2, q);
5314
5315 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005316 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005317 if (!c)
5318 continue;
5319 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5320 USTPUTC(CTLESC, q);
5321 USTPUTC(c, q);
5322 }
5323
5324 expdest = q;
5325}
5326
5327static void
5328strtodest(const char *p, int syntax, int quotes)
5329{
5330 memtodest(p, strlen(p), syntax, quotes);
5331}
5332
5333/*
5334 * Record the fact that we have to scan this region of the
5335 * string for IFS characters.
5336 */
5337static void
5338recordregion(int start, int end, int nulonly)
5339{
5340 struct ifsregion *ifsp;
5341
5342 if (ifslastp == NULL) {
5343 ifsp = &ifsfirst;
5344 } else {
5345 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005346 ifsp = ckzalloc(sizeof(*ifsp));
5347 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005348 ifslastp->next = ifsp;
5349 INT_ON;
5350 }
5351 ifslastp = ifsp;
5352 ifslastp->begoff = start;
5353 ifslastp->endoff = end;
5354 ifslastp->nulonly = nulonly;
5355}
5356
5357static void
5358removerecordregions(int endoff)
5359{
5360 if (ifslastp == NULL)
5361 return;
5362
5363 if (ifsfirst.endoff > endoff) {
5364 while (ifsfirst.next != NULL) {
5365 struct ifsregion *ifsp;
5366 INT_OFF;
5367 ifsp = ifsfirst.next->next;
5368 free(ifsfirst.next);
5369 ifsfirst.next = ifsp;
5370 INT_ON;
5371 }
5372 if (ifsfirst.begoff > endoff)
5373 ifslastp = NULL;
5374 else {
5375 ifslastp = &ifsfirst;
5376 ifsfirst.endoff = endoff;
5377 }
5378 return;
5379 }
5380
5381 ifslastp = &ifsfirst;
5382 while (ifslastp->next && ifslastp->next->begoff < endoff)
5383 ifslastp=ifslastp->next;
5384 while (ifslastp->next != NULL) {
5385 struct ifsregion *ifsp;
5386 INT_OFF;
5387 ifsp = ifslastp->next->next;
5388 free(ifslastp->next);
5389 ifslastp->next = ifsp;
5390 INT_ON;
5391 }
5392 if (ifslastp->endoff > endoff)
5393 ifslastp->endoff = endoff;
5394}
5395
5396static char *
5397exptilde(char *startp, char *p, int flag)
5398{
5399 char c;
5400 char *name;
5401 struct passwd *pw;
5402 const char *home;
5403 int quotes = flag & (EXP_FULL | EXP_CASE);
5404 int startloc;
5405
5406 name = p + 1;
5407
5408 while ((c = *++p) != '\0') {
5409 switch (c) {
5410 case CTLESC:
5411 return startp;
5412 case CTLQUOTEMARK:
5413 return startp;
5414 case ':':
5415 if (flag & EXP_VARTILDE)
5416 goto done;
5417 break;
5418 case '/':
5419 case CTLENDVAR:
5420 goto done;
5421 }
5422 }
5423 done:
5424 *p = '\0';
5425 if (*name == '\0') {
5426 home = lookupvar(homestr);
5427 } else {
5428 pw = getpwnam(name);
5429 if (pw == NULL)
5430 goto lose;
5431 home = pw->pw_dir;
5432 }
5433 if (!home || !*home)
5434 goto lose;
5435 *p = c;
5436 startloc = expdest - (char *)stackblock();
5437 strtodest(home, SQSYNTAX, quotes);
5438 recordregion(startloc, expdest - (char *)stackblock(), 0);
5439 return p;
5440 lose:
5441 *p = c;
5442 return startp;
5443}
5444
5445/*
5446 * Execute a command inside back quotes. If it's a builtin command, we
5447 * want to save its output in a block obtained from malloc. Otherwise
5448 * we fork off a subprocess and get the output of the command via a pipe.
5449 * Should be called with interrupts off.
5450 */
5451struct backcmd { /* result of evalbackcmd */
5452 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005453 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005454 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005455 struct job *jp; /* job structure for command */
5456};
5457
5458/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005459static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005460#define EV_EXIT 01 /* exit after evaluating tree */
5461static void evaltree(union node *, int);
5462
5463static void
5464evalbackcmd(union node *n, struct backcmd *result)
5465{
5466 int saveherefd;
5467
5468 result->fd = -1;
5469 result->buf = NULL;
5470 result->nleft = 0;
5471 result->jp = NULL;
5472 if (n == NULL) {
5473 goto out;
5474 }
5475
5476 saveherefd = herefd;
5477 herefd = -1;
5478
5479 {
5480 int pip[2];
5481 struct job *jp;
5482
5483 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005484 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005485 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005486 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5487 FORCE_INT_ON;
5488 close(pip[0]);
5489 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005490 /*close(1);*/
5491 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005492 close(pip[1]);
5493 }
5494 eflag = 0;
5495 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5496 /* NOTREACHED */
5497 }
5498 close(pip[1]);
5499 result->fd = pip[0];
5500 result->jp = jp;
5501 }
5502 herefd = saveherefd;
5503 out:
5504 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5505 result->fd, result->buf, result->nleft, result->jp));
5506}
5507
5508/*
5509 * Expand stuff in backwards quotes.
5510 */
5511static void
5512expbackq(union node *cmd, int quoted, int quotes)
5513{
5514 struct backcmd in;
5515 int i;
5516 char buf[128];
5517 char *p;
5518 char *dest;
5519 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005520 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005521 struct stackmark smark;
5522
5523 INT_OFF;
5524 setstackmark(&smark);
5525 dest = expdest;
5526 startloc = dest - (char *)stackblock();
5527 grabstackstr(dest);
5528 evalbackcmd(cmd, &in);
5529 popstackmark(&smark);
5530
5531 p = in.buf;
5532 i = in.nleft;
5533 if (i == 0)
5534 goto read;
5535 for (;;) {
5536 memtodest(p, i, syntax, quotes);
5537 read:
5538 if (in.fd < 0)
5539 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005540 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005541 TRACE(("expbackq: read returns %d\n", i));
5542 if (i <= 0)
5543 break;
5544 p = buf;
5545 }
5546
Denis Vlasenko60818682007-09-28 22:07:23 +00005547 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005548 if (in.fd >= 0) {
5549 close(in.fd);
5550 back_exitstatus = waitforjob(in.jp);
5551 }
5552 INT_ON;
5553
5554 /* Eat all trailing newlines */
5555 dest = expdest;
5556 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5557 STUNPUTC(dest);
5558 expdest = dest;
5559
5560 if (quoted == 0)
5561 recordregion(startloc, dest - (char *)stackblock(), 0);
5562 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5563 (dest - (char *)stackblock()) - startloc,
5564 (dest - (char *)stackblock()) - startloc,
5565 stackblock() + startloc));
5566}
5567
5568#if ENABLE_ASH_MATH_SUPPORT
5569/*
5570 * Expand arithmetic expression. Backup to start of expression,
5571 * evaluate, place result in (backed up) result, adjust string position.
5572 */
5573static void
5574expari(int quotes)
5575{
5576 char *p, *start;
5577 int begoff;
5578 int flag;
5579 int len;
5580
5581 /* ifsfree(); */
5582
5583 /*
5584 * This routine is slightly over-complicated for
5585 * efficiency. Next we scan backwards looking for the
5586 * start of arithmetic.
5587 */
5588 start = stackblock();
5589 p = expdest - 1;
5590 *p = '\0';
5591 p--;
5592 do {
5593 int esc;
5594
5595 while (*p != CTLARI) {
5596 p--;
5597#if DEBUG
5598 if (p < start) {
5599 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5600 }
5601#endif
5602 }
5603
5604 esc = esclen(start, p);
5605 if (!(esc % 2)) {
5606 break;
5607 }
5608
5609 p -= esc + 1;
5610 } while (1);
5611
5612 begoff = p - start;
5613
5614 removerecordregions(begoff);
5615
5616 flag = p[1];
5617
5618 expdest = p;
5619
5620 if (quotes)
5621 rmescapes(p + 2);
5622
5623 len = cvtnum(dash_arith(p + 2));
5624
5625 if (flag != '"')
5626 recordregion(begoff, begoff + len, 0);
5627}
5628#endif
5629
5630/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005631static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005632
5633/*
5634 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5635 * characters to allow for further processing. Otherwise treat
5636 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005637 *
5638 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5639 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5640 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005641 */
5642static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005643argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005644{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005645 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005646 '=',
5647 ':',
5648 CTLQUOTEMARK,
5649 CTLENDVAR,
5650 CTLESC,
5651 CTLVAR,
5652 CTLBACKQ,
5653 CTLBACKQ | CTLQUOTE,
5654#if ENABLE_ASH_MATH_SUPPORT
5655 CTLENDARI,
5656#endif
5657 0
5658 };
5659 const char *reject = spclchars;
5660 int c;
5661 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5662 int breakall = flag & EXP_WORD;
5663 int inquotes;
5664 size_t length;
5665 int startloc;
5666
5667 if (!(flag & EXP_VARTILDE)) {
5668 reject += 2;
5669 } else if (flag & EXP_VARTILDE2) {
5670 reject++;
5671 }
5672 inquotes = 0;
5673 length = 0;
5674 if (flag & EXP_TILDE) {
5675 char *q;
5676
5677 flag &= ~EXP_TILDE;
5678 tilde:
5679 q = p;
5680 if (*q == CTLESC && (flag & EXP_QWORD))
5681 q++;
5682 if (*q == '~')
5683 p = exptilde(p, q, flag);
5684 }
5685 start:
5686 startloc = expdest - (char *)stackblock();
5687 for (;;) {
5688 length += strcspn(p + length, reject);
5689 c = p[length];
5690 if (c && (!(c & 0x80)
5691#if ENABLE_ASH_MATH_SUPPORT
5692 || c == CTLENDARI
5693#endif
5694 )) {
5695 /* c == '=' || c == ':' || c == CTLENDARI */
5696 length++;
5697 }
5698 if (length > 0) {
5699 int newloc;
5700 expdest = stack_nputstr(p, length, expdest);
5701 newloc = expdest - (char *)stackblock();
5702 if (breakall && !inquotes && newloc > startloc) {
5703 recordregion(startloc, newloc, 0);
5704 }
5705 startloc = newloc;
5706 }
5707 p += length + 1;
5708 length = 0;
5709
5710 switch (c) {
5711 case '\0':
5712 goto breakloop;
5713 case '=':
5714 if (flag & EXP_VARTILDE2) {
5715 p--;
5716 continue;
5717 }
5718 flag |= EXP_VARTILDE2;
5719 reject++;
5720 /* fall through */
5721 case ':':
5722 /*
5723 * sort of a hack - expand tildes in variable
5724 * assignments (after the first '=' and after ':'s).
5725 */
5726 if (*--p == '~') {
5727 goto tilde;
5728 }
5729 continue;
5730 }
5731
5732 switch (c) {
5733 case CTLENDVAR: /* ??? */
5734 goto breakloop;
5735 case CTLQUOTEMARK:
5736 /* "$@" syntax adherence hack */
5737 if (
5738 !inquotes &&
5739 !memcmp(p, dolatstr, 4) &&
5740 (p[4] == CTLQUOTEMARK || (
5741 p[4] == CTLENDVAR &&
5742 p[5] == CTLQUOTEMARK
5743 ))
5744 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005745 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005746 goto start;
5747 }
5748 inquotes = !inquotes;
5749 addquote:
5750 if (quotes) {
5751 p--;
5752 length++;
5753 startloc++;
5754 }
5755 break;
5756 case CTLESC:
5757 startloc++;
5758 length++;
5759 goto addquote;
5760 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005761 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005762 goto start;
5763 case CTLBACKQ:
5764 c = 0;
5765 case CTLBACKQ|CTLQUOTE:
5766 expbackq(argbackq->n, c, quotes);
5767 argbackq = argbackq->next;
5768 goto start;
5769#if ENABLE_ASH_MATH_SUPPORT
5770 case CTLENDARI:
5771 p--;
5772 expari(quotes);
5773 goto start;
5774#endif
5775 }
5776 }
5777 breakloop:
5778 ;
5779}
5780
5781static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005782scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005783 int zero)
5784{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005785// This commented out code was added by James Simmons <jsimmons@infradead.org>
5786// as part of a larger change when he added support for ${var/a/b}.
5787// However, it broke # and % operators:
5788//
5789//var=ababcdcd
5790// ok bad
5791//echo ${var#ab} abcdcd abcdcd
5792//echo ${var##ab} abcdcd abcdcd
5793//echo ${var#a*b} abcdcd ababcdcd (!)
5794//echo ${var##a*b} cdcd cdcd
5795//echo ${var#?} babcdcd ababcdcd (!)
5796//echo ${var##?} babcdcd babcdcd
5797//echo ${var#*} ababcdcd babcdcd (!)
5798//echo ${var##*}
5799//echo ${var%cd} ababcd ababcd
5800//echo ${var%%cd} ababcd abab (!)
5801//echo ${var%c*d} ababcd ababcd
5802//echo ${var%%c*d} abab ababcdcd (!)
5803//echo ${var%?} ababcdc ababcdc
5804//echo ${var%%?} ababcdc ababcdcd (!)
5805//echo ${var%*} ababcdcd ababcdcd
5806//echo ${var%%*}
5807//
5808// Commenting it back out helped. Remove it completely if it really
5809// is not needed.
5810
5811 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005812 char c;
5813
5814 loc = startp;
5815 loc2 = rmesc;
5816 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005817 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005818 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005819
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005820 c = *loc2;
5821 if (zero) {
5822 *loc2 = '\0';
5823 s = rmesc;
5824 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005825 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005826
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005827// // chop off end if its '*'
5828// full = strrchr(str, '*');
5829// if (full && full != str)
5830// match--;
5831//
5832// // If str starts with '*' replace with s.
5833// if ((*str == '*') && strlen(s) >= match) {
5834// full = xstrdup(s);
5835// strncpy(full+strlen(s)-match+1, str+1, match-1);
5836// } else
5837// full = xstrndup(str, match);
5838// match = strncmp(s, full, strlen(full));
5839// free(full);
5840//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005841 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005842 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005843 return loc;
5844 if (quotes && *loc == CTLESC)
5845 loc++;
5846 loc++;
5847 loc2++;
5848 } while (c);
5849 return 0;
5850}
5851
5852static char *
5853scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5854 int zero)
5855{
5856 int esc = 0;
5857 char *loc;
5858 char *loc2;
5859
5860 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5861 int match;
5862 char c = *loc2;
5863 const char *s = loc2;
5864 if (zero) {
5865 *loc2 = '\0';
5866 s = rmesc;
5867 }
5868 match = pmatch(str, s);
5869 *loc2 = c;
5870 if (match)
5871 return loc;
5872 loc--;
5873 if (quotes) {
5874 if (--esc < 0) {
5875 esc = esclen(startp, loc);
5876 }
5877 if (esc % 2) {
5878 esc--;
5879 loc--;
5880 }
5881 }
5882 }
5883 return 0;
5884}
5885
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005886static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005887static void
5888varunset(const char *end, const char *var, const char *umsg, int varflags)
5889{
5890 const char *msg;
5891 const char *tail;
5892
5893 tail = nullstr;
5894 msg = "parameter not set";
5895 if (umsg) {
5896 if (*end == CTLENDVAR) {
5897 if (varflags & VSNUL)
5898 tail = " or null";
5899 } else
5900 msg = umsg;
5901 }
5902 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5903}
5904
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005905#if ENABLE_ASH_BASH_COMPAT
5906static char *
5907parse_sub_pattern(char *arg, int inquotes)
5908{
5909 char *idx, *repl = NULL;
5910 unsigned char c;
5911
Denis Vlasenko2659c632008-06-14 06:04:59 +00005912 idx = arg;
5913 while (1) {
5914 c = *arg;
5915 if (!c)
5916 break;
5917 if (c == '/') {
5918 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005919 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005920 repl = idx + 1;
5921 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005922 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005923 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005924 *idx++ = c;
5925 if (!inquotes && c == '\\' && arg[1] == '\\')
5926 arg++; /* skip both \\, not just first one */
5927 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005928 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005929 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005930
5931 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005932}
5933#endif /* ENABLE_ASH_BASH_COMPAT */
5934
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005935static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005936subevalvar(char *p, char *str, int strloc, int subtype,
5937 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005938{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005939 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005940 char *startp;
5941 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005942 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005943 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5944 USE_ASH_BASH_COMPAT(char null = '\0';)
5945 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5946 int saveherefd = herefd;
5947 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005948 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005949 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005950
5951 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005952 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5953 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005954 STPUTC('\0', expdest);
5955 herefd = saveherefd;
5956 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005957 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958
5959 switch (subtype) {
5960 case VSASSIGN:
5961 setvar(str, startp, 0);
5962 amount = startp - expdest;
5963 STADJUST(amount, expdest);
5964 return startp;
5965
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005966#if ENABLE_ASH_BASH_COMPAT
5967 case VSSUBSTR:
5968 loc = str = stackblock() + strloc;
5969// TODO: number() instead? It does error checking...
5970 pos = atoi(loc);
5971 len = str - startp - 1;
5972
5973 /* *loc != '\0', guaranteed by parser */
5974 if (quotes) {
5975 char *ptr;
5976
5977 /* We must adjust the length by the number of escapes we find. */
5978 for (ptr = startp; ptr < (str - 1); ptr++) {
5979 if(*ptr == CTLESC) {
5980 len--;
5981 ptr++;
5982 }
5983 }
5984 }
5985 orig_len = len;
5986
5987 if (*loc++ == ':') {
5988// TODO: number() instead? It does error checking...
5989 len = atoi(loc);
5990 } else {
5991 len = orig_len;
5992 while (*loc && *loc != ':')
5993 loc++;
5994 if (*loc++ == ':')
5995// TODO: number() instead? It does error checking...
5996 len = atoi(loc);
5997 }
5998 if (pos >= orig_len) {
5999 pos = 0;
6000 len = 0;
6001 }
6002 if (len > (orig_len - pos))
6003 len = orig_len - pos;
6004
6005 for (str = startp; pos; str++, pos--) {
6006 if (quotes && *str == CTLESC)
6007 str++;
6008 }
6009 for (loc = startp; len; len--) {
6010 if (quotes && *str == CTLESC)
6011 *loc++ = *str++;
6012 *loc++ = *str++;
6013 }
6014 *loc = '\0';
6015 amount = loc - expdest;
6016 STADJUST(amount, expdest);
6017 return loc;
6018#endif
6019
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006020 case VSQUESTION:
6021 varunset(p, str, startp, varflags);
6022 /* NOTREACHED */
6023 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006024 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006026 /* We'll comeback here if we grow the stack while handling
6027 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6028 * stack will need rebasing, and we'll need to remove our work
6029 * areas each time
6030 */
6031 USE_ASH_BASH_COMPAT(restart:)
6032
6033 amount = expdest - ((char *)stackblock() + resetloc);
6034 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006035 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036
6037 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006038 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006039 if (quotes) {
6040 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6041 if (rmesc != startp) {
6042 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006043 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006044 }
6045 }
6046 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006047 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006049 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006050
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006051#if ENABLE_ASH_BASH_COMPAT
6052 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6053 char *idx, *end, *restart_detect;
6054
6055 if(!repl) {
6056 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6057 if (!repl)
6058 repl = &null;
6059 }
6060
6061 /* If there's no pattern to match, return the expansion unmolested */
6062 if (*str == '\0')
6063 return 0;
6064
6065 len = 0;
6066 idx = startp;
6067 end = str - 1;
6068 while (idx < end) {
6069 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6070 if (!loc) {
6071 /* No match, advance */
6072 restart_detect = stackblock();
6073 STPUTC(*idx, expdest);
6074 if (quotes && *idx == CTLESC) {
6075 idx++;
6076 len++;
6077 STPUTC(*idx, expdest);
6078 }
6079 if (stackblock() != restart_detect)
6080 goto restart;
6081 idx++;
6082 len++;
6083 rmesc++;
6084 continue;
6085 }
6086
6087 if (subtype == VSREPLACEALL) {
6088 while (idx < loc) {
6089 if (quotes && *idx == CTLESC)
6090 idx++;
6091 idx++;
6092 rmesc++;
6093 }
6094 } else
6095 idx = loc;
6096
6097 for (loc = repl; *loc; loc++) {
6098 restart_detect = stackblock();
6099 STPUTC(*loc, expdest);
6100 if (stackblock() != restart_detect)
6101 goto restart;
6102 len++;
6103 }
6104
6105 if (subtype == VSREPLACE) {
6106 while (*idx) {
6107 restart_detect = stackblock();
6108 STPUTC(*idx, expdest);
6109 if (stackblock() != restart_detect)
6110 goto restart;
6111 len++;
6112 idx++;
6113 }
6114 break;
6115 }
6116 }
6117
6118 /* We've put the replaced text into a buffer at workloc, now
6119 * move it to the right place and adjust the stack.
6120 */
6121 startp = stackblock() + startloc;
6122 STPUTC('\0', expdest);
6123 memmove(startp, stackblock() + workloc, len);
6124 startp[len++] = '\0';
6125 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6126 STADJUST(-amount, expdest);
6127 return startp;
6128 }
6129#endif /* ENABLE_ASH_BASH_COMPAT */
6130
6131 subtype -= VSTRIMRIGHT;
6132#if DEBUG
6133 if (subtype < 0 || subtype > 7)
6134 abort();
6135#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006136 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6137 zero = subtype >> 1;
6138 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6139 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6140
6141 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6142 if (loc) {
6143 if (zero) {
6144 memmove(startp, loc, str - loc);
6145 loc = startp + (str - loc) - 1;
6146 }
6147 *loc = '\0';
6148 amount = loc - expdest;
6149 STADJUST(amount, expdest);
6150 }
6151 return loc;
6152}
6153
6154/*
6155 * Add the value of a specialized variable to the stack string.
6156 */
6157static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006158varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006159{
6160 int num;
6161 char *p;
6162 int i;
6163 int sep = 0;
6164 int sepq = 0;
6165 ssize_t len = 0;
6166 char **ap;
6167 int syntax;
6168 int quoted = varflags & VSQUOTE;
6169 int subtype = varflags & VSTYPE;
6170 int quotes = flags & (EXP_FULL | EXP_CASE);
6171
6172 if (quoted && (flags & EXP_FULL))
6173 sep = 1 << CHAR_BIT;
6174
6175 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6176 switch (*name) {
6177 case '$':
6178 num = rootpid;
6179 goto numvar;
6180 case '?':
6181 num = exitstatus;
6182 goto numvar;
6183 case '#':
6184 num = shellparam.nparam;
6185 goto numvar;
6186 case '!':
6187 num = backgndpid;
6188 if (num == 0)
6189 return -1;
6190 numvar:
6191 len = cvtnum(num);
6192 break;
6193 case '-':
6194 p = makestrspace(NOPTS, expdest);
6195 for (i = NOPTS - 1; i >= 0; i--) {
6196 if (optlist[i]) {
6197 USTPUTC(optletters(i), p);
6198 len++;
6199 }
6200 }
6201 expdest = p;
6202 break;
6203 case '@':
6204 if (sep)
6205 goto param;
6206 /* fall through */
6207 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006208 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006209 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6210 sepq = 1;
6211 param:
6212 ap = shellparam.p;
6213 if (!ap)
6214 return -1;
6215 while ((p = *ap++)) {
6216 size_t partlen;
6217
6218 partlen = strlen(p);
6219 len += partlen;
6220
6221 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6222 memtodest(p, partlen, syntax, quotes);
6223
6224 if (*ap && sep) {
6225 char *q;
6226
6227 len++;
6228 if (subtype == VSPLUS || subtype == VSLENGTH) {
6229 continue;
6230 }
6231 q = expdest;
6232 if (sepq)
6233 STPUTC(CTLESC, q);
6234 STPUTC(sep, q);
6235 expdest = q;
6236 }
6237 }
6238 return len;
6239 case '0':
6240 case '1':
6241 case '2':
6242 case '3':
6243 case '4':
6244 case '5':
6245 case '6':
6246 case '7':
6247 case '8':
6248 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006249// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006250 num = atoi(name);
6251 if (num < 0 || num > shellparam.nparam)
6252 return -1;
6253 p = num ? shellparam.p[num - 1] : arg0;
6254 goto value;
6255 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006256 /* NB: name has form "VAR=..." */
6257
6258 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6259 * which should be considered before we check variables. */
6260 if (var_str_list) {
6261 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6262 p = NULL;
6263 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006264 char *str, *eq;
6265 str = var_str_list->text;
6266 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006267 if (!eq) /* stop at first non-assignment */
6268 break;
6269 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006270 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006271 && strncmp(str, name, name_len) == 0) {
6272 p = eq;
6273 /* goto value; - WRONG! */
6274 /* think "A=1 A=2 B=$A" */
6275 }
6276 var_str_list = var_str_list->next;
6277 } while (var_str_list);
6278 if (p)
6279 goto value;
6280 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006281 p = lookupvar(name);
6282 value:
6283 if (!p)
6284 return -1;
6285
6286 len = strlen(p);
6287 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6288 memtodest(p, len, syntax, quotes);
6289 return len;
6290 }
6291
6292 if (subtype == VSPLUS || subtype == VSLENGTH)
6293 STADJUST(-len, expdest);
6294 return len;
6295}
6296
6297/*
6298 * Expand a variable, and return a pointer to the next character in the
6299 * input string.
6300 */
6301static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006302evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006303{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006304 char varflags;
6305 char subtype;
6306 char quoted;
6307 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006308 char *var;
6309 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006310 int startloc;
6311 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006312
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006313 varflags = *p++;
6314 subtype = varflags & VSTYPE;
6315 quoted = varflags & VSQUOTE;
6316 var = p;
6317 easy = (!quoted || (*var == '@' && shellparam.nparam));
6318 startloc = expdest - (char *)stackblock();
6319 p = strchr(p, '=') + 1;
6320
6321 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006322 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006323 if (varflags & VSNUL)
6324 varlen--;
6325
6326 if (subtype == VSPLUS) {
6327 varlen = -1 - varlen;
6328 goto vsplus;
6329 }
6330
6331 if (subtype == VSMINUS) {
6332 vsplus:
6333 if (varlen < 0) {
6334 argstr(
6335 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006336 (quoted ? EXP_QWORD : EXP_WORD),
6337 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006338 );
6339 goto end;
6340 }
6341 if (easy)
6342 goto record;
6343 goto end;
6344 }
6345
6346 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6347 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006348 if (subevalvar(p, var, /* strloc: */ 0,
6349 subtype, startloc, varflags,
6350 /* quotes: */ 0,
6351 var_str_list)
6352 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006353 varflags &= ~VSNUL;
6354 /*
6355 * Remove any recorded regions beyond
6356 * start of variable
6357 */
6358 removerecordregions(startloc);
6359 goto again;
6360 }
6361 goto end;
6362 }
6363 if (easy)
6364 goto record;
6365 goto end;
6366 }
6367
6368 if (varlen < 0 && uflag)
6369 varunset(p, var, 0, 0);
6370
6371 if (subtype == VSLENGTH) {
6372 cvtnum(varlen > 0 ? varlen : 0);
6373 goto record;
6374 }
6375
6376 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006377 if (easy)
6378 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006379 goto end;
6380 }
6381
6382#if DEBUG
6383 switch (subtype) {
6384 case VSTRIMLEFT:
6385 case VSTRIMLEFTMAX:
6386 case VSTRIMRIGHT:
6387 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006388#if ENABLE_ASH_BASH_COMPAT
6389 case VSSUBSTR:
6390 case VSREPLACE:
6391 case VSREPLACEALL:
6392#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006393 break;
6394 default:
6395 abort();
6396 }
6397#endif
6398
6399 if (varlen >= 0) {
6400 /*
6401 * Terminate the string and start recording the pattern
6402 * right after it
6403 */
6404 STPUTC('\0', expdest);
6405 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006406 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6407 startloc, varflags,
6408 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6409 var_str_list)
6410 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006411 int amount = expdest - (
6412 (char *)stackblock() + patloc - 1
6413 );
6414 STADJUST(-amount, expdest);
6415 }
6416 /* Remove any recorded regions beyond start of variable */
6417 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006418 record:
6419 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006420 }
6421
6422 end:
6423 if (subtype != VSNORMAL) { /* skip to end of alternative */
6424 int nesting = 1;
6425 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006426 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006427 if (c == CTLESC)
6428 p++;
6429 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6430 if (varlen >= 0)
6431 argbackq = argbackq->next;
6432 } else if (c == CTLVAR) {
6433 if ((*p++ & VSTYPE) != VSNORMAL)
6434 nesting++;
6435 } else if (c == CTLENDVAR) {
6436 if (--nesting == 0)
6437 break;
6438 }
6439 }
6440 }
6441 return p;
6442}
6443
6444/*
6445 * Break the argument string into pieces based upon IFS and add the
6446 * strings to the argument list. The regions of the string to be
6447 * searched for IFS characters have been stored by recordregion.
6448 */
6449static void
6450ifsbreakup(char *string, struct arglist *arglist)
6451{
6452 struct ifsregion *ifsp;
6453 struct strlist *sp;
6454 char *start;
6455 char *p;
6456 char *q;
6457 const char *ifs, *realifs;
6458 int ifsspc;
6459 int nulonly;
6460
6461 start = string;
6462 if (ifslastp != NULL) {
6463 ifsspc = 0;
6464 nulonly = 0;
6465 realifs = ifsset() ? ifsval() : defifs;
6466 ifsp = &ifsfirst;
6467 do {
6468 p = string + ifsp->begoff;
6469 nulonly = ifsp->nulonly;
6470 ifs = nulonly ? nullstr : realifs;
6471 ifsspc = 0;
6472 while (p < string + ifsp->endoff) {
6473 q = p;
6474 if (*p == CTLESC)
6475 p++;
6476 if (!strchr(ifs, *p)) {
6477 p++;
6478 continue;
6479 }
6480 if (!nulonly)
6481 ifsspc = (strchr(defifs, *p) != NULL);
6482 /* Ignore IFS whitespace at start */
6483 if (q == start && ifsspc) {
6484 p++;
6485 start = p;
6486 continue;
6487 }
6488 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006489 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006490 sp->text = start;
6491 *arglist->lastp = sp;
6492 arglist->lastp = &sp->next;
6493 p++;
6494 if (!nulonly) {
6495 for (;;) {
6496 if (p >= string + ifsp->endoff) {
6497 break;
6498 }
6499 q = p;
6500 if (*p == CTLESC)
6501 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006502 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006503 p = q;
6504 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006505 }
6506 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006507 if (ifsspc) {
6508 p++;
6509 ifsspc = 0;
6510 } else {
6511 p = q;
6512 break;
6513 }
6514 } else
6515 p++;
6516 }
6517 }
6518 start = p;
6519 } /* while */
6520 ifsp = ifsp->next;
6521 } while (ifsp != NULL);
6522 if (nulonly)
6523 goto add;
6524 }
6525
6526 if (!*start)
6527 return;
6528
6529 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006530 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006531 sp->text = start;
6532 *arglist->lastp = sp;
6533 arglist->lastp = &sp->next;
6534}
6535
6536static void
6537ifsfree(void)
6538{
6539 struct ifsregion *p;
6540
6541 INT_OFF;
6542 p = ifsfirst.next;
6543 do {
6544 struct ifsregion *ifsp;
6545 ifsp = p->next;
6546 free(p);
6547 p = ifsp;
6548 } while (p);
6549 ifslastp = NULL;
6550 ifsfirst.next = NULL;
6551 INT_ON;
6552}
6553
6554/*
6555 * Add a file name to the list.
6556 */
6557static void
6558addfname(const char *name)
6559{
6560 struct strlist *sp;
6561
Denis Vlasenko597906c2008-02-20 16:38:54 +00006562 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006563 sp->text = ststrdup(name);
6564 *exparg.lastp = sp;
6565 exparg.lastp = &sp->next;
6566}
6567
6568static char *expdir;
6569
6570/*
6571 * Do metacharacter (i.e. *, ?, [...]) expansion.
6572 */
6573static void
6574expmeta(char *enddir, char *name)
6575{
6576 char *p;
6577 const char *cp;
6578 char *start;
6579 char *endname;
6580 int metaflag;
6581 struct stat statb;
6582 DIR *dirp;
6583 struct dirent *dp;
6584 int atend;
6585 int matchdot;
6586
6587 metaflag = 0;
6588 start = name;
6589 for (p = name; *p; p++) {
6590 if (*p == '*' || *p == '?')
6591 metaflag = 1;
6592 else if (*p == '[') {
6593 char *q = p + 1;
6594 if (*q == '!')
6595 q++;
6596 for (;;) {
6597 if (*q == '\\')
6598 q++;
6599 if (*q == '/' || *q == '\0')
6600 break;
6601 if (*++q == ']') {
6602 metaflag = 1;
6603 break;
6604 }
6605 }
6606 } else if (*p == '\\')
6607 p++;
6608 else if (*p == '/') {
6609 if (metaflag)
6610 goto out;
6611 start = p + 1;
6612 }
6613 }
6614 out:
6615 if (metaflag == 0) { /* we've reached the end of the file name */
6616 if (enddir != expdir)
6617 metaflag++;
6618 p = name;
6619 do {
6620 if (*p == '\\')
6621 p++;
6622 *enddir++ = *p;
6623 } while (*p++);
6624 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6625 addfname(expdir);
6626 return;
6627 }
6628 endname = p;
6629 if (name < start) {
6630 p = name;
6631 do {
6632 if (*p == '\\')
6633 p++;
6634 *enddir++ = *p++;
6635 } while (p < start);
6636 }
6637 if (enddir == expdir) {
6638 cp = ".";
6639 } else if (enddir == expdir + 1 && *expdir == '/') {
6640 cp = "/";
6641 } else {
6642 cp = expdir;
6643 enddir[-1] = '\0';
6644 }
6645 dirp = opendir(cp);
6646 if (dirp == NULL)
6647 return;
6648 if (enddir != expdir)
6649 enddir[-1] = '/';
6650 if (*endname == 0) {
6651 atend = 1;
6652 } else {
6653 atend = 0;
6654 *endname++ = '\0';
6655 }
6656 matchdot = 0;
6657 p = start;
6658 if (*p == '\\')
6659 p++;
6660 if (*p == '.')
6661 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006662 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006663 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006664 continue;
6665 if (pmatch(start, dp->d_name)) {
6666 if (atend) {
6667 strcpy(enddir, dp->d_name);
6668 addfname(expdir);
6669 } else {
6670 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6671 continue;
6672 p[-1] = '/';
6673 expmeta(p, endname);
6674 }
6675 }
6676 }
6677 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006678 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006679 endname[-1] = '/';
6680}
6681
6682static struct strlist *
6683msort(struct strlist *list, int len)
6684{
6685 struct strlist *p, *q = NULL;
6686 struct strlist **lpp;
6687 int half;
6688 int n;
6689
6690 if (len <= 1)
6691 return list;
6692 half = len >> 1;
6693 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006694 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006695 q = p;
6696 p = p->next;
6697 }
6698 q->next = NULL; /* terminate first half of list */
6699 q = msort(list, half); /* sort first half of list */
6700 p = msort(p, len - half); /* sort second half */
6701 lpp = &list;
6702 for (;;) {
6703#if ENABLE_LOCALE_SUPPORT
6704 if (strcoll(p->text, q->text) < 0)
6705#else
6706 if (strcmp(p->text, q->text) < 0)
6707#endif
6708 {
6709 *lpp = p;
6710 lpp = &p->next;
6711 p = *lpp;
6712 if (p == NULL) {
6713 *lpp = q;
6714 break;
6715 }
6716 } else {
6717 *lpp = q;
6718 lpp = &q->next;
6719 q = *lpp;
6720 if (q == NULL) {
6721 *lpp = p;
6722 break;
6723 }
6724 }
6725 }
6726 return list;
6727}
6728
6729/*
6730 * Sort the results of file name expansion. It calculates the number of
6731 * strings to sort and then calls msort (short for merge sort) to do the
6732 * work.
6733 */
6734static struct strlist *
6735expsort(struct strlist *str)
6736{
6737 int len;
6738 struct strlist *sp;
6739
6740 len = 0;
6741 for (sp = str; sp; sp = sp->next)
6742 len++;
6743 return msort(str, len);
6744}
6745
6746static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006747expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006748{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006749 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006750 '*', '?', '[', 0
6751 };
6752 /* TODO - EXP_REDIR */
6753
6754 while (str) {
6755 struct strlist **savelastp;
6756 struct strlist *sp;
6757 char *p;
6758
6759 if (fflag)
6760 goto nometa;
6761 if (!strpbrk(str->text, metachars))
6762 goto nometa;
6763 savelastp = exparg.lastp;
6764
6765 INT_OFF;
6766 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6767 {
6768 int i = strlen(str->text);
6769 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6770 }
6771
6772 expmeta(expdir, p);
6773 free(expdir);
6774 if (p != str->text)
6775 free(p);
6776 INT_ON;
6777 if (exparg.lastp == savelastp) {
6778 /*
6779 * no matches
6780 */
6781 nometa:
6782 *exparg.lastp = str;
6783 rmescapes(str->text);
6784 exparg.lastp = &str->next;
6785 } else {
6786 *exparg.lastp = NULL;
6787 *savelastp = sp = expsort(*savelastp);
6788 while (sp->next != NULL)
6789 sp = sp->next;
6790 exparg.lastp = &sp->next;
6791 }
6792 str = str->next;
6793 }
6794}
6795
6796/*
6797 * Perform variable substitution and command substitution on an argument,
6798 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6799 * perform splitting and file name expansion. When arglist is NULL, perform
6800 * here document expansion.
6801 */
6802static void
6803expandarg(union node *arg, struct arglist *arglist, int flag)
6804{
6805 struct strlist *sp;
6806 char *p;
6807
6808 argbackq = arg->narg.backquote;
6809 STARTSTACKSTR(expdest);
6810 ifsfirst.next = NULL;
6811 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006812 argstr(arg->narg.text, flag,
6813 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006814 p = _STPUTC('\0', expdest);
6815 expdest = p - 1;
6816 if (arglist == NULL) {
6817 return; /* here document expanded */
6818 }
6819 p = grabstackstr(p);
6820 exparg.lastp = &exparg.list;
6821 /*
6822 * TODO - EXP_REDIR
6823 */
6824 if (flag & EXP_FULL) {
6825 ifsbreakup(p, &exparg);
6826 *exparg.lastp = NULL;
6827 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006828 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006829 } else {
6830 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6831 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006832 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006833 sp->text = p;
6834 *exparg.lastp = sp;
6835 exparg.lastp = &sp->next;
6836 }
6837 if (ifsfirst.next)
6838 ifsfree();
6839 *exparg.lastp = NULL;
6840 if (exparg.list) {
6841 *arglist->lastp = exparg.list;
6842 arglist->lastp = exparg.lastp;
6843 }
6844}
6845
6846/*
6847 * Expand shell variables and backquotes inside a here document.
6848 */
6849static void
6850expandhere(union node *arg, int fd)
6851{
6852 herefd = fd;
6853 expandarg(arg, (struct arglist *)NULL, 0);
6854 full_write(fd, stackblock(), expdest - (char *)stackblock());
6855}
6856
6857/*
6858 * Returns true if the pattern matches the string.
6859 */
6860static int
6861patmatch(char *pattern, const char *string)
6862{
6863 return pmatch(preglob(pattern, 0, 0), string);
6864}
6865
6866/*
6867 * See if a pattern matches in a case statement.
6868 */
6869static int
6870casematch(union node *pattern, char *val)
6871{
6872 struct stackmark smark;
6873 int result;
6874
6875 setstackmark(&smark);
6876 argbackq = pattern->narg.backquote;
6877 STARTSTACKSTR(expdest);
6878 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006879 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6880 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006881 STACKSTRNUL(expdest);
6882 result = patmatch(stackblock(), val);
6883 popstackmark(&smark);
6884 return result;
6885}
6886
6887
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006888/* ============ find_command */
6889
6890struct builtincmd {
6891 const char *name;
6892 int (*builtin)(int, char **);
6893 /* unsigned flags; */
6894};
6895#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006896/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006897 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006898#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006899#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006900
6901struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006902 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006903 union param {
6904 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006905 /* index >= 0 for commands without path (slashes) */
6906 /* (TODO: what exactly does the value mean? PATH position?) */
6907 /* index == -1 for commands with slashes */
6908 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006909 const struct builtincmd *cmd;
6910 struct funcnode *func;
6911 } u;
6912};
6913/* values of cmdtype */
6914#define CMDUNKNOWN -1 /* no entry in table for command */
6915#define CMDNORMAL 0 /* command is an executable program */
6916#define CMDFUNCTION 1 /* command is a shell function */
6917#define CMDBUILTIN 2 /* command is a shell builtin */
6918
6919/* action to find_command() */
6920#define DO_ERR 0x01 /* prints errors */
6921#define DO_ABS 0x02 /* checks absolute paths */
6922#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6923#define DO_ALTPATH 0x08 /* using alternate path */
6924#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6925
6926static void find_command(char *, struct cmdentry *, int, const char *);
6927
6928
6929/* ============ Hashing commands */
6930
6931/*
6932 * When commands are first encountered, they are entered in a hash table.
6933 * This ensures that a full path search will not have to be done for them
6934 * on each invocation.
6935 *
6936 * We should investigate converting to a linear search, even though that
6937 * would make the command name "hash" a misnomer.
6938 */
6939
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006940struct tblentry {
6941 struct tblentry *next; /* next entry in hash chain */
6942 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006943 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006944 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006945 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006946};
6947
Denis Vlasenko01631112007-12-16 17:20:38 +00006948static struct tblentry **cmdtable;
6949#define INIT_G_cmdtable() do { \
6950 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6951} while (0)
6952
6953static int builtinloc = -1; /* index in path of %builtin, or -1 */
6954
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006955
6956static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006957tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006958{
6959 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006960
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006961#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00006962 if (applet_no >= 0) {
6963 if (APPLET_IS_NOEXEC(applet_no))
6964 run_applet_no_and_exit(applet_no, argv);
6965 /* re-exec ourselves with the new arguments */
6966 execve(bb_busybox_exec_path, argv, envp);
6967 /* If they called chroot or otherwise made the binary no longer
6968 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006969 }
6970#endif
6971
6972 repeat:
6973#ifdef SYSV
6974 do {
6975 execve(cmd, argv, envp);
6976 } while (errno == EINTR);
6977#else
6978 execve(cmd, argv, envp);
6979#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006980 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006981 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006982 return;
6983 }
6984 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006985 char **ap;
6986 char **new;
6987
6988 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006989 continue;
6990 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006991 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006992 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006993 ap += 2;
6994 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006995 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00006996 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006997 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006998 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006999 goto repeat;
7000 }
7001}
7002
7003/*
7004 * Exec a program. Never returns. If you change this routine, you may
7005 * have to change the find_command routine as well.
7006 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007007static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007008static void
7009shellexec(char **argv, const char *path, int idx)
7010{
7011 char *cmdname;
7012 int e;
7013 char **envp;
7014 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007015#if ENABLE_FEATURE_SH_STANDALONE
7016 int applet_no = -1;
7017#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007018
7019 clearredir(1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007020 envp = listvars(VEXPORT, VUNSET, 0);
7021 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007022#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007023 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007024#endif
7025 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007026 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007027 e = errno;
7028 } else {
7029 e = ENOENT;
7030 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7031 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007032 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007033 if (errno != ENOENT && errno != ENOTDIR)
7034 e = errno;
7035 }
7036 stunalloc(cmdname);
7037 }
7038 }
7039
7040 /* Map to POSIX errors */
7041 switch (e) {
7042 case EACCES:
7043 exerrno = 126;
7044 break;
7045 case ENOENT:
7046 exerrno = 127;
7047 break;
7048 default:
7049 exerrno = 2;
7050 break;
7051 }
7052 exitstatus = exerrno;
7053 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007054 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007055 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7056 /* NOTREACHED */
7057}
7058
7059static void
7060printentry(struct tblentry *cmdp)
7061{
7062 int idx;
7063 const char *path;
7064 char *name;
7065
7066 idx = cmdp->param.index;
7067 path = pathval();
7068 do {
7069 name = padvance(&path, cmdp->cmdname);
7070 stunalloc(name);
7071 } while (--idx >= 0);
7072 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7073}
7074
7075/*
7076 * Clear out command entries. The argument specifies the first entry in
7077 * PATH which has changed.
7078 */
7079static void
7080clearcmdentry(int firstchange)
7081{
7082 struct tblentry **tblp;
7083 struct tblentry **pp;
7084 struct tblentry *cmdp;
7085
7086 INT_OFF;
7087 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7088 pp = tblp;
7089 while ((cmdp = *pp) != NULL) {
7090 if ((cmdp->cmdtype == CMDNORMAL &&
7091 cmdp->param.index >= firstchange)
7092 || (cmdp->cmdtype == CMDBUILTIN &&
7093 builtinloc >= firstchange)
7094 ) {
7095 *pp = cmdp->next;
7096 free(cmdp);
7097 } else {
7098 pp = &cmdp->next;
7099 }
7100 }
7101 }
7102 INT_ON;
7103}
7104
7105/*
7106 * Locate a command in the command hash table. If "add" is nonzero,
7107 * add the command to the table if it is not already present. The
7108 * variable "lastcmdentry" is set to point to the address of the link
7109 * pointing to the entry, so that delete_cmd_entry can delete the
7110 * entry.
7111 *
7112 * Interrupts must be off if called with add != 0.
7113 */
7114static struct tblentry **lastcmdentry;
7115
7116static struct tblentry *
7117cmdlookup(const char *name, int add)
7118{
7119 unsigned int hashval;
7120 const char *p;
7121 struct tblentry *cmdp;
7122 struct tblentry **pp;
7123
7124 p = name;
7125 hashval = (unsigned char)*p << 4;
7126 while (*p)
7127 hashval += (unsigned char)*p++;
7128 hashval &= 0x7FFF;
7129 pp = &cmdtable[hashval % CMDTABLESIZE];
7130 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7131 if (strcmp(cmdp->cmdname, name) == 0)
7132 break;
7133 pp = &cmdp->next;
7134 }
7135 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007136 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7137 + strlen(name)
7138 /* + 1 - already done because
7139 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007140 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007141 cmdp->cmdtype = CMDUNKNOWN;
7142 strcpy(cmdp->cmdname, name);
7143 }
7144 lastcmdentry = pp;
7145 return cmdp;
7146}
7147
7148/*
7149 * Delete the command entry returned on the last lookup.
7150 */
7151static void
7152delete_cmd_entry(void)
7153{
7154 struct tblentry *cmdp;
7155
7156 INT_OFF;
7157 cmdp = *lastcmdentry;
7158 *lastcmdentry = cmdp->next;
7159 if (cmdp->cmdtype == CMDFUNCTION)
7160 freefunc(cmdp->param.func);
7161 free(cmdp);
7162 INT_ON;
7163}
7164
7165/*
7166 * Add a new command entry, replacing any existing command entry for
7167 * the same name - except special builtins.
7168 */
7169static void
7170addcmdentry(char *name, struct cmdentry *entry)
7171{
7172 struct tblentry *cmdp;
7173
7174 cmdp = cmdlookup(name, 1);
7175 if (cmdp->cmdtype == CMDFUNCTION) {
7176 freefunc(cmdp->param.func);
7177 }
7178 cmdp->cmdtype = entry->cmdtype;
7179 cmdp->param = entry->u;
7180 cmdp->rehash = 0;
7181}
7182
7183static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007184hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007185{
7186 struct tblentry **pp;
7187 struct tblentry *cmdp;
7188 int c;
7189 struct cmdentry entry;
7190 char *name;
7191
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007192 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007193 clearcmdentry(0);
7194 return 0;
7195 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007196
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007197 if (*argptr == NULL) {
7198 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7199 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7200 if (cmdp->cmdtype == CMDNORMAL)
7201 printentry(cmdp);
7202 }
7203 }
7204 return 0;
7205 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007206
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007207 c = 0;
7208 while ((name = *argptr) != NULL) {
7209 cmdp = cmdlookup(name, 0);
7210 if (cmdp != NULL
7211 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007212 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7213 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007214 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007215 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007216 find_command(name, &entry, DO_ERR, pathval());
7217 if (entry.cmdtype == CMDUNKNOWN)
7218 c = 1;
7219 argptr++;
7220 }
7221 return c;
7222}
7223
7224/*
7225 * Called when a cd is done. Marks all commands so the next time they
7226 * are executed they will be rehashed.
7227 */
7228static void
7229hashcd(void)
7230{
7231 struct tblentry **pp;
7232 struct tblentry *cmdp;
7233
7234 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7235 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007236 if (cmdp->cmdtype == CMDNORMAL
7237 || (cmdp->cmdtype == CMDBUILTIN
7238 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7239 && builtinloc > 0)
7240 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007241 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007242 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007243 }
7244 }
7245}
7246
7247/*
7248 * Fix command hash table when PATH changed.
7249 * Called before PATH is changed. The argument is the new value of PATH;
7250 * pathval() still returns the old value at this point.
7251 * Called with interrupts off.
7252 */
7253static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007254changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007255{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007256 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007257 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007258 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007259 int idx_bltin;
7260
7261 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007262 firstchange = 9999; /* assume no change */
7263 idx = 0;
7264 idx_bltin = -1;
7265 for (;;) {
7266 if (*old != *new) {
7267 firstchange = idx;
7268 if ((*old == '\0' && *new == ':')
7269 || (*old == ':' && *new == '\0'))
7270 firstchange++;
7271 old = new; /* ignore subsequent differences */
7272 }
7273 if (*new == '\0')
7274 break;
7275 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7276 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007277 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007278 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007279 new++, old++;
7280 }
7281 if (builtinloc < 0 && idx_bltin >= 0)
7282 builtinloc = idx_bltin; /* zap builtins */
7283 if (builtinloc >= 0 && idx_bltin < 0)
7284 firstchange = 0;
7285 clearcmdentry(firstchange);
7286 builtinloc = idx_bltin;
7287}
7288
7289#define TEOF 0
7290#define TNL 1
7291#define TREDIR 2
7292#define TWORD 3
7293#define TSEMI 4
7294#define TBACKGND 5
7295#define TAND 6
7296#define TOR 7
7297#define TPIPE 8
7298#define TLP 9
7299#define TRP 10
7300#define TENDCASE 11
7301#define TENDBQUOTE 12
7302#define TNOT 13
7303#define TCASE 14
7304#define TDO 15
7305#define TDONE 16
7306#define TELIF 17
7307#define TELSE 18
7308#define TESAC 19
7309#define TFI 20
7310#define TFOR 21
7311#define TIF 22
7312#define TIN 23
7313#define TTHEN 24
7314#define TUNTIL 25
7315#define TWHILE 26
7316#define TBEGIN 27
7317#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007318typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007319
7320/* first char is indicating which tokens mark the end of a list */
7321static const char *const tokname_array[] = {
7322 "\1end of file",
7323 "\0newline",
7324 "\0redirection",
7325 "\0word",
7326 "\0;",
7327 "\0&",
7328 "\0&&",
7329 "\0||",
7330 "\0|",
7331 "\0(",
7332 "\1)",
7333 "\1;;",
7334 "\1`",
7335#define KWDOFFSET 13
7336 /* the following are keywords */
7337 "\0!",
7338 "\0case",
7339 "\1do",
7340 "\1done",
7341 "\1elif",
7342 "\1else",
7343 "\1esac",
7344 "\1fi",
7345 "\0for",
7346 "\0if",
7347 "\0in",
7348 "\1then",
7349 "\0until",
7350 "\0while",
7351 "\0{",
7352 "\1}",
7353};
7354
7355static const char *
7356tokname(int tok)
7357{
7358 static char buf[16];
7359
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007360//try this:
7361//if (tok < TSEMI) return tokname_array[tok] + 1;
7362//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7363//return buf;
7364
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007365 if (tok >= TSEMI)
7366 buf[0] = '"';
7367 sprintf(buf + (tok >= TSEMI), "%s%c",
7368 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7369 return buf;
7370}
7371
7372/* Wrapper around strcmp for qsort/bsearch/... */
7373static int
7374pstrcmp(const void *a, const void *b)
7375{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007376 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007377}
7378
7379static const char *const *
7380findkwd(const char *s)
7381{
7382 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007383 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7384 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007385}
7386
7387/*
7388 * Locate and print what a word is...
7389 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390static int
7391describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007392{
7393 struct cmdentry entry;
7394 struct tblentry *cmdp;
7395#if ENABLE_ASH_ALIAS
7396 const struct alias *ap;
7397#endif
7398 const char *path = pathval();
7399
7400 if (describe_command_verbose) {
7401 out1str(command);
7402 }
7403
7404 /* First look at the keywords */
7405 if (findkwd(command)) {
7406 out1str(describe_command_verbose ? " is a shell keyword" : command);
7407 goto out;
7408 }
7409
7410#if ENABLE_ASH_ALIAS
7411 /* Then look at the aliases */
7412 ap = lookupalias(command, 0);
7413 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007414 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007415 out1str("alias ");
7416 printalias(ap);
7417 return 0;
7418 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007419 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007420 goto out;
7421 }
7422#endif
7423 /* Then check if it is a tracked alias */
7424 cmdp = cmdlookup(command, 0);
7425 if (cmdp != NULL) {
7426 entry.cmdtype = cmdp->cmdtype;
7427 entry.u = cmdp->param;
7428 } else {
7429 /* Finally use brute force */
7430 find_command(command, &entry, DO_ABS, path);
7431 }
7432
7433 switch (entry.cmdtype) {
7434 case CMDNORMAL: {
7435 int j = entry.u.index;
7436 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007437 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007438 p = command;
7439 } else {
7440 do {
7441 p = padvance(&path, command);
7442 stunalloc(p);
7443 } while (--j >= 0);
7444 }
7445 if (describe_command_verbose) {
7446 out1fmt(" is%s %s",
7447 (cmdp ? " a tracked alias for" : nullstr), p
7448 );
7449 } else {
7450 out1str(p);
7451 }
7452 break;
7453 }
7454
7455 case CMDFUNCTION:
7456 if (describe_command_verbose) {
7457 out1str(" is a shell function");
7458 } else {
7459 out1str(command);
7460 }
7461 break;
7462
7463 case CMDBUILTIN:
7464 if (describe_command_verbose) {
7465 out1fmt(" is a %sshell builtin",
7466 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7467 "special " : nullstr
7468 );
7469 } else {
7470 out1str(command);
7471 }
7472 break;
7473
7474 default:
7475 if (describe_command_verbose) {
7476 out1str(": not found\n");
7477 }
7478 return 127;
7479 }
7480 out:
7481 outstr("\n", stdout);
7482 return 0;
7483}
7484
7485static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007486typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007487{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007488 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007489 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007490 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007491
Denis Vlasenko46846e22007-05-20 13:08:31 +00007492 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007493 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007494 i++;
7495 verbose = 0;
7496 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007497 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007498 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007499 }
7500 return err;
7501}
7502
7503#if ENABLE_ASH_CMDCMD
7504static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007505commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007506{
7507 int c;
7508 enum {
7509 VERIFY_BRIEF = 1,
7510 VERIFY_VERBOSE = 2,
7511 } verify = 0;
7512
7513 while ((c = nextopt("pvV")) != '\0')
7514 if (c == 'V')
7515 verify |= VERIFY_VERBOSE;
7516 else if (c == 'v')
7517 verify |= VERIFY_BRIEF;
7518#if DEBUG
7519 else if (c != 'p')
7520 abort();
7521#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007522 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7523 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007524 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007525 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007526
7527 return 0;
7528}
7529#endif
7530
7531
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007532/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007533
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007534static int funcblocksize; /* size of structures in function */
7535static int funcstringsize; /* size of strings in node */
7536static void *funcblock; /* block to allocate function from */
7537static char *funcstring; /* block to allocate strings from */
7538
Eric Andersencb57d552001-06-28 07:25:16 +00007539/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007540#define EV_EXIT 01 /* exit after evaluating tree */
7541#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7542#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007543
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007544static const short nodesize[26] = {
7545 SHELL_ALIGN(sizeof(struct ncmd)),
7546 SHELL_ALIGN(sizeof(struct npipe)),
7547 SHELL_ALIGN(sizeof(struct nredir)),
7548 SHELL_ALIGN(sizeof(struct nredir)),
7549 SHELL_ALIGN(sizeof(struct nredir)),
7550 SHELL_ALIGN(sizeof(struct nbinary)),
7551 SHELL_ALIGN(sizeof(struct nbinary)),
7552 SHELL_ALIGN(sizeof(struct nbinary)),
7553 SHELL_ALIGN(sizeof(struct nif)),
7554 SHELL_ALIGN(sizeof(struct nbinary)),
7555 SHELL_ALIGN(sizeof(struct nbinary)),
7556 SHELL_ALIGN(sizeof(struct nfor)),
7557 SHELL_ALIGN(sizeof(struct ncase)),
7558 SHELL_ALIGN(sizeof(struct nclist)),
7559 SHELL_ALIGN(sizeof(struct narg)),
7560 SHELL_ALIGN(sizeof(struct narg)),
7561 SHELL_ALIGN(sizeof(struct nfile)),
7562 SHELL_ALIGN(sizeof(struct nfile)),
7563 SHELL_ALIGN(sizeof(struct nfile)),
7564 SHELL_ALIGN(sizeof(struct nfile)),
7565 SHELL_ALIGN(sizeof(struct nfile)),
7566 SHELL_ALIGN(sizeof(struct ndup)),
7567 SHELL_ALIGN(sizeof(struct ndup)),
7568 SHELL_ALIGN(sizeof(struct nhere)),
7569 SHELL_ALIGN(sizeof(struct nhere)),
7570 SHELL_ALIGN(sizeof(struct nnot)),
7571};
7572
7573static void calcsize(union node *n);
7574
7575static void
7576sizenodelist(struct nodelist *lp)
7577{
7578 while (lp) {
7579 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7580 calcsize(lp->n);
7581 lp = lp->next;
7582 }
7583}
7584
7585static void
7586calcsize(union node *n)
7587{
7588 if (n == NULL)
7589 return;
7590 funcblocksize += nodesize[n->type];
7591 switch (n->type) {
7592 case NCMD:
7593 calcsize(n->ncmd.redirect);
7594 calcsize(n->ncmd.args);
7595 calcsize(n->ncmd.assign);
7596 break;
7597 case NPIPE:
7598 sizenodelist(n->npipe.cmdlist);
7599 break;
7600 case NREDIR:
7601 case NBACKGND:
7602 case NSUBSHELL:
7603 calcsize(n->nredir.redirect);
7604 calcsize(n->nredir.n);
7605 break;
7606 case NAND:
7607 case NOR:
7608 case NSEMI:
7609 case NWHILE:
7610 case NUNTIL:
7611 calcsize(n->nbinary.ch2);
7612 calcsize(n->nbinary.ch1);
7613 break;
7614 case NIF:
7615 calcsize(n->nif.elsepart);
7616 calcsize(n->nif.ifpart);
7617 calcsize(n->nif.test);
7618 break;
7619 case NFOR:
7620 funcstringsize += strlen(n->nfor.var) + 1;
7621 calcsize(n->nfor.body);
7622 calcsize(n->nfor.args);
7623 break;
7624 case NCASE:
7625 calcsize(n->ncase.cases);
7626 calcsize(n->ncase.expr);
7627 break;
7628 case NCLIST:
7629 calcsize(n->nclist.body);
7630 calcsize(n->nclist.pattern);
7631 calcsize(n->nclist.next);
7632 break;
7633 case NDEFUN:
7634 case NARG:
7635 sizenodelist(n->narg.backquote);
7636 funcstringsize += strlen(n->narg.text) + 1;
7637 calcsize(n->narg.next);
7638 break;
7639 case NTO:
7640 case NCLOBBER:
7641 case NFROM:
7642 case NFROMTO:
7643 case NAPPEND:
7644 calcsize(n->nfile.fname);
7645 calcsize(n->nfile.next);
7646 break;
7647 case NTOFD:
7648 case NFROMFD:
7649 calcsize(n->ndup.vname);
7650 calcsize(n->ndup.next);
7651 break;
7652 case NHERE:
7653 case NXHERE:
7654 calcsize(n->nhere.doc);
7655 calcsize(n->nhere.next);
7656 break;
7657 case NNOT:
7658 calcsize(n->nnot.com);
7659 break;
7660 };
7661}
7662
7663static char *
7664nodeckstrdup(char *s)
7665{
7666 char *rtn = funcstring;
7667
7668 strcpy(funcstring, s);
7669 funcstring += strlen(s) + 1;
7670 return rtn;
7671}
7672
7673static union node *copynode(union node *);
7674
7675static struct nodelist *
7676copynodelist(struct nodelist *lp)
7677{
7678 struct nodelist *start;
7679 struct nodelist **lpp;
7680
7681 lpp = &start;
7682 while (lp) {
7683 *lpp = funcblock;
7684 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7685 (*lpp)->n = copynode(lp->n);
7686 lp = lp->next;
7687 lpp = &(*lpp)->next;
7688 }
7689 *lpp = NULL;
7690 return start;
7691}
7692
7693static union node *
7694copynode(union node *n)
7695{
7696 union node *new;
7697
7698 if (n == NULL)
7699 return NULL;
7700 new = funcblock;
7701 funcblock = (char *) funcblock + nodesize[n->type];
7702
7703 switch (n->type) {
7704 case NCMD:
7705 new->ncmd.redirect = copynode(n->ncmd.redirect);
7706 new->ncmd.args = copynode(n->ncmd.args);
7707 new->ncmd.assign = copynode(n->ncmd.assign);
7708 break;
7709 case NPIPE:
7710 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007711 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007712 break;
7713 case NREDIR:
7714 case NBACKGND:
7715 case NSUBSHELL:
7716 new->nredir.redirect = copynode(n->nredir.redirect);
7717 new->nredir.n = copynode(n->nredir.n);
7718 break;
7719 case NAND:
7720 case NOR:
7721 case NSEMI:
7722 case NWHILE:
7723 case NUNTIL:
7724 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7725 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7726 break;
7727 case NIF:
7728 new->nif.elsepart = copynode(n->nif.elsepart);
7729 new->nif.ifpart = copynode(n->nif.ifpart);
7730 new->nif.test = copynode(n->nif.test);
7731 break;
7732 case NFOR:
7733 new->nfor.var = nodeckstrdup(n->nfor.var);
7734 new->nfor.body = copynode(n->nfor.body);
7735 new->nfor.args = copynode(n->nfor.args);
7736 break;
7737 case NCASE:
7738 new->ncase.cases = copynode(n->ncase.cases);
7739 new->ncase.expr = copynode(n->ncase.expr);
7740 break;
7741 case NCLIST:
7742 new->nclist.body = copynode(n->nclist.body);
7743 new->nclist.pattern = copynode(n->nclist.pattern);
7744 new->nclist.next = copynode(n->nclist.next);
7745 break;
7746 case NDEFUN:
7747 case NARG:
7748 new->narg.backquote = copynodelist(n->narg.backquote);
7749 new->narg.text = nodeckstrdup(n->narg.text);
7750 new->narg.next = copynode(n->narg.next);
7751 break;
7752 case NTO:
7753 case NCLOBBER:
7754 case NFROM:
7755 case NFROMTO:
7756 case NAPPEND:
7757 new->nfile.fname = copynode(n->nfile.fname);
7758 new->nfile.fd = n->nfile.fd;
7759 new->nfile.next = copynode(n->nfile.next);
7760 break;
7761 case NTOFD:
7762 case NFROMFD:
7763 new->ndup.vname = copynode(n->ndup.vname);
7764 new->ndup.dupfd = n->ndup.dupfd;
7765 new->ndup.fd = n->ndup.fd;
7766 new->ndup.next = copynode(n->ndup.next);
7767 break;
7768 case NHERE:
7769 case NXHERE:
7770 new->nhere.doc = copynode(n->nhere.doc);
7771 new->nhere.fd = n->nhere.fd;
7772 new->nhere.next = copynode(n->nhere.next);
7773 break;
7774 case NNOT:
7775 new->nnot.com = copynode(n->nnot.com);
7776 break;
7777 };
7778 new->type = n->type;
7779 return new;
7780}
7781
7782/*
7783 * Make a copy of a parse tree.
7784 */
7785static struct funcnode *
7786copyfunc(union node *n)
7787{
7788 struct funcnode *f;
7789 size_t blocksize;
7790
7791 funcblocksize = offsetof(struct funcnode, n);
7792 funcstringsize = 0;
7793 calcsize(n);
7794 blocksize = funcblocksize;
7795 f = ckmalloc(blocksize + funcstringsize);
7796 funcblock = (char *) f + offsetof(struct funcnode, n);
7797 funcstring = (char *) f + blocksize;
7798 copynode(n);
7799 f->count = 0;
7800 return f;
7801}
7802
7803/*
7804 * Define a shell function.
7805 */
7806static void
7807defun(char *name, union node *func)
7808{
7809 struct cmdentry entry;
7810
7811 INT_OFF;
7812 entry.cmdtype = CMDFUNCTION;
7813 entry.u.func = copyfunc(func);
7814 addcmdentry(name, &entry);
7815 INT_ON;
7816}
7817
7818static int evalskip; /* set if we are skipping commands */
7819/* reasons for skipping commands (see comment on breakcmd routine) */
7820#define SKIPBREAK (1 << 0)
7821#define SKIPCONT (1 << 1)
7822#define SKIPFUNC (1 << 2)
7823#define SKIPFILE (1 << 3)
7824#define SKIPEVAL (1 << 4)
7825static int skipcount; /* number of levels to skip */
7826static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007827static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007828
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007829/* forward decl way out to parsing code - dotrap needs it */
7830static int evalstring(char *s, int mask);
7831
7832/*
7833 * Called to execute a trap. Perhaps we should avoid entering new trap
7834 * handlers while we are executing a trap handler.
7835 */
7836static int
7837dotrap(void)
7838{
7839 char *p;
7840 char *q;
7841 int i;
7842 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007843 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007844
7845 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007846 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007847 xbarrier();
7848
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007849 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007850 if (!*q)
7851 continue;
7852 *q = '\0';
7853
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007854 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007855 if (!p)
7856 continue;
7857 skip = evalstring(p, SKIPEVAL);
7858 exitstatus = savestatus;
7859 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007860 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007861 }
7862
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007863 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007864}
7865
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007866/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007867static void evalloop(union node *, int);
7868static void evalfor(union node *, int);
7869static void evalcase(union node *, int);
7870static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007871static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007872static void evalpipe(union node *, int);
7873static void evalcommand(union node *, int);
7874static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007875static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007876
Eric Andersen62483552001-07-10 06:09:16 +00007877/*
Eric Andersenc470f442003-07-28 09:56:35 +00007878 * Evaluate a parse tree. The value is left in the global variable
7879 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007880 */
Eric Andersenc470f442003-07-28 09:56:35 +00007881static void
7882evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007883{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007884
7885 struct jmploc *volatile savehandler = exception_handler;
7886 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007887 int checkexit = 0;
7888 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007889 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007890
Eric Andersenc470f442003-07-28 09:56:35 +00007891 if (n == NULL) {
7892 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007893 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007894 }
Eric Andersenc470f442003-07-28 09:56:35 +00007895 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007896 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007897
7898 exception_handler = &jmploc;
7899 {
7900 int err = setjmp(jmploc.loc);
7901 if (err) {
7902 /* if it was a signal, check for trap handlers */
7903 if (exception == EXSIG)
7904 goto out;
7905 /* continue on the way out */
7906 exception_handler = savehandler;
7907 longjmp(exception_handler->loc, err);
7908 }
7909 }
7910
Eric Andersenc470f442003-07-28 09:56:35 +00007911 switch (n->type) {
7912 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007913#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007914 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007915 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007916 break;
7917#endif
7918 case NNOT:
7919 evaltree(n->nnot.com, EV_TESTED);
7920 status = !exitstatus;
7921 goto setstatus;
7922 case NREDIR:
7923 expredir(n->nredir.redirect);
7924 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7925 if (!status) {
7926 evaltree(n->nredir.n, flags & EV_TESTED);
7927 status = exitstatus;
7928 }
7929 popredir(0);
7930 goto setstatus;
7931 case NCMD:
7932 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007933 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007934 if (eflag && !(flags & EV_TESTED))
7935 checkexit = ~0;
7936 goto calleval;
7937 case NFOR:
7938 evalfn = evalfor;
7939 goto calleval;
7940 case NWHILE:
7941 case NUNTIL:
7942 evalfn = evalloop;
7943 goto calleval;
7944 case NSUBSHELL:
7945 case NBACKGND:
7946 evalfn = evalsubshell;
7947 goto calleval;
7948 case NPIPE:
7949 evalfn = evalpipe;
7950 goto checkexit;
7951 case NCASE:
7952 evalfn = evalcase;
7953 goto calleval;
7954 case NAND:
7955 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007956 case NSEMI: {
7957
Eric Andersenc470f442003-07-28 09:56:35 +00007958#if NAND + 1 != NOR
7959#error NAND + 1 != NOR
7960#endif
7961#if NOR + 1 != NSEMI
7962#error NOR + 1 != NSEMI
7963#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00007964 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00007965 evaltree(
7966 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007967 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00007968 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007969 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00007970 break;
7971 if (!evalskip) {
7972 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007973 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007974 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007975 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007976 evalfn(n, flags);
7977 break;
7978 }
7979 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007980 }
Eric Andersenc470f442003-07-28 09:56:35 +00007981 case NIF:
7982 evaltree(n->nif.test, EV_TESTED);
7983 if (evalskip)
7984 break;
7985 if (exitstatus == 0) {
7986 n = n->nif.ifpart;
7987 goto evaln;
7988 } else if (n->nif.elsepart) {
7989 n = n->nif.elsepart;
7990 goto evaln;
7991 }
7992 goto success;
7993 case NDEFUN:
7994 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007995 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007996 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007997 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007998 exitstatus = status;
7999 break;
8000 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008001
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008002 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008003 exception_handler = savehandler;
8004 out1:
8005 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008006 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008007 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008008 goto exexit;
8009
8010 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008011 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008012 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008013 }
Eric Andersen62483552001-07-10 06:09:16 +00008014}
8015
Eric Andersenc470f442003-07-28 09:56:35 +00008016#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8017static
8018#endif
8019void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8020
Eric Andersenc470f442003-07-28 09:56:35 +00008021static void
8022evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008023{
8024 int status;
8025
8026 loopnest++;
8027 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008028 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008029 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008030 int i;
8031
Eric Andersencb57d552001-06-28 07:25:16 +00008032 evaltree(n->nbinary.ch1, EV_TESTED);
8033 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008034 skipping:
8035 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008036 evalskip = 0;
8037 continue;
8038 }
8039 if (evalskip == SKIPBREAK && --skipcount <= 0)
8040 evalskip = 0;
8041 break;
8042 }
Eric Andersenc470f442003-07-28 09:56:35 +00008043 i = exitstatus;
8044 if (n->type != NWHILE)
8045 i = !i;
8046 if (i != 0)
8047 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008048 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008049 status = exitstatus;
8050 if (evalskip)
8051 goto skipping;
8052 }
8053 loopnest--;
8054 exitstatus = status;
8055}
8056
Eric Andersenc470f442003-07-28 09:56:35 +00008057static void
8058evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008059{
8060 struct arglist arglist;
8061 union node *argp;
8062 struct strlist *sp;
8063 struct stackmark smark;
8064
8065 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008066 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008067 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008068 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008069 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008070 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008071 if (evalskip)
8072 goto out;
8073 }
8074 *arglist.lastp = NULL;
8075
8076 exitstatus = 0;
8077 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008078 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008079 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008080 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008081 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008082 if (evalskip) {
8083 if (evalskip == SKIPCONT && --skipcount <= 0) {
8084 evalskip = 0;
8085 continue;
8086 }
8087 if (evalskip == SKIPBREAK && --skipcount <= 0)
8088 evalskip = 0;
8089 break;
8090 }
8091 }
8092 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008093 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008094 popstackmark(&smark);
8095}
8096
Eric Andersenc470f442003-07-28 09:56:35 +00008097static void
8098evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008099{
8100 union node *cp;
8101 union node *patp;
8102 struct arglist arglist;
8103 struct stackmark smark;
8104
8105 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008106 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008107 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008108 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008109 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008110 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8111 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008112 if (casematch(patp, arglist.list->text)) {
8113 if (evalskip == 0) {
8114 evaltree(cp->nclist.body, flags);
8115 }
8116 goto out;
8117 }
8118 }
8119 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008120 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008121 popstackmark(&smark);
8122}
8123
Eric Andersenc470f442003-07-28 09:56:35 +00008124/*
8125 * Kick off a subshell to evaluate a tree.
8126 */
Eric Andersenc470f442003-07-28 09:56:35 +00008127static void
8128evalsubshell(union node *n, int flags)
8129{
8130 struct job *jp;
8131 int backgnd = (n->type == NBACKGND);
8132 int status;
8133
8134 expredir(n->nredir.redirect);
8135 if (!backgnd && flags & EV_EXIT && !trap[0])
8136 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008137 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008138 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008139 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008140 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008141 flags |= EV_EXIT;
8142 if (backgnd)
8143 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008144 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008145 redirect(n->nredir.redirect, 0);
8146 evaltreenr(n->nredir.n, flags);
8147 /* never returns */
8148 }
8149 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008150 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008151 status = waitforjob(jp);
8152 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008153 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008154}
8155
Eric Andersenc470f442003-07-28 09:56:35 +00008156/*
8157 * Compute the names of the files in a redirection list.
8158 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008159static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008160static void
8161expredir(union node *n)
8162{
8163 union node *redir;
8164
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008165 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008166 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008167
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008168 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008169 fn.lastp = &fn.list;
8170 switch (redir->type) {
8171 case NFROMTO:
8172 case NFROM:
8173 case NTO:
8174 case NCLOBBER:
8175 case NAPPEND:
8176 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8177 redir->nfile.expfname = fn.list->text;
8178 break;
8179 case NFROMFD:
8180 case NTOFD:
8181 if (redir->ndup.vname) {
8182 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008183 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008184 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008185 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008186 }
8187 break;
8188 }
8189 }
8190}
8191
Eric Andersencb57d552001-06-28 07:25:16 +00008192/*
Eric Andersencb57d552001-06-28 07:25:16 +00008193 * Evaluate a pipeline. All the processes in the pipeline are children
8194 * of the process creating the pipeline. (This differs from some versions
8195 * of the shell, which make the last process in a pipeline the parent
8196 * of all the rest.)
8197 */
Eric Andersenc470f442003-07-28 09:56:35 +00008198static void
8199evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008200{
8201 struct job *jp;
8202 struct nodelist *lp;
8203 int pipelen;
8204 int prevfd;
8205 int pip[2];
8206
Eric Andersenc470f442003-07-28 09:56:35 +00008207 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008208 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008209 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008210 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008211 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008212 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008213 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008214 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008215 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008216 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008217 pip[1] = -1;
8218 if (lp->next) {
8219 if (pipe(pip) < 0) {
8220 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008221 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008222 }
8223 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008224 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008225 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008226 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008227 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008228 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008229 if (prevfd > 0) {
8230 dup2(prevfd, 0);
8231 close(prevfd);
8232 }
8233 if (pip[1] > 1) {
8234 dup2(pip[1], 1);
8235 close(pip[1]);
8236 }
Eric Andersenc470f442003-07-28 09:56:35 +00008237 evaltreenr(lp->n, flags);
8238 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008239 }
8240 if (prevfd >= 0)
8241 close(prevfd);
8242 prevfd = pip[0];
8243 close(pip[1]);
8244 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008245 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008246 exitstatus = waitforjob(jp);
8247 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008248 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008249 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008250}
8251
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008252/*
8253 * Controls whether the shell is interactive or not.
8254 */
8255static void
8256setinteractive(int on)
8257{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008258 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008259
8260 if (++on == is_interactive)
8261 return;
8262 is_interactive = on;
8263 setsignal(SIGINT);
8264 setsignal(SIGQUIT);
8265 setsignal(SIGTERM);
8266#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8267 if (is_interactive > 1) {
8268 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008269 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008270
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008271 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008272 out1fmt(
8273 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008274 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008275 "Enter 'help' for a list of built-in commands."
8276 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008277 bb_banner);
8278 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008279 }
8280 }
8281#endif
8282}
8283
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008284static void
8285optschanged(void)
8286{
8287#if DEBUG
8288 opentrace();
8289#endif
8290 setinteractive(iflag);
8291 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008292#if ENABLE_FEATURE_EDITING_VI
8293 if (viflag)
8294 line_input_state->flags |= VI_MODE;
8295 else
8296 line_input_state->flags &= ~VI_MODE;
8297#else
8298 viflag = 0; /* forcibly keep the option off */
8299#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008300}
8301
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008302static struct localvar *localvars;
8303
8304/*
8305 * Called after a function returns.
8306 * Interrupts must be off.
8307 */
8308static void
8309poplocalvars(void)
8310{
8311 struct localvar *lvp;
8312 struct var *vp;
8313
8314 while ((lvp = localvars) != NULL) {
8315 localvars = lvp->next;
8316 vp = lvp->vp;
8317 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8318 if (vp == NULL) { /* $- saved */
8319 memcpy(optlist, lvp->text, sizeof(optlist));
8320 free((char*)lvp->text);
8321 optschanged();
8322 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8323 unsetvar(vp->text);
8324 } else {
8325 if (vp->func)
8326 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8327 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8328 free((char*)vp->text);
8329 vp->flags = lvp->flags;
8330 vp->text = lvp->text;
8331 }
8332 free(lvp);
8333 }
8334}
8335
8336static int
8337evalfun(struct funcnode *func, int argc, char **argv, int flags)
8338{
8339 volatile struct shparam saveparam;
8340 struct localvar *volatile savelocalvars;
8341 struct jmploc *volatile savehandler;
8342 struct jmploc jmploc;
8343 int e;
8344
8345 saveparam = shellparam;
8346 savelocalvars = localvars;
8347 e = setjmp(jmploc.loc);
8348 if (e) {
8349 goto funcdone;
8350 }
8351 INT_OFF;
8352 savehandler = exception_handler;
8353 exception_handler = &jmploc;
8354 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008355 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008356 func->count++;
8357 funcnest++;
8358 INT_ON;
8359 shellparam.nparam = argc - 1;
8360 shellparam.p = argv + 1;
8361#if ENABLE_ASH_GETOPTS
8362 shellparam.optind = 1;
8363 shellparam.optoff = -1;
8364#endif
8365 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008366 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008367 INT_OFF;
8368 funcnest--;
8369 freefunc(func);
8370 poplocalvars();
8371 localvars = savelocalvars;
8372 freeparam(&shellparam);
8373 shellparam = saveparam;
8374 exception_handler = savehandler;
8375 INT_ON;
8376 evalskip &= ~SKIPFUNC;
8377 return e;
8378}
8379
Denis Vlasenko131ae172007-02-18 13:00:19 +00008380#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008381static char **
8382parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008383{
8384 char *cp, c;
8385
8386 for (;;) {
8387 cp = *++argv;
8388 if (!cp)
8389 return 0;
8390 if (*cp++ != '-')
8391 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008392 c = *cp++;
8393 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008394 break;
8395 if (c == '-' && !*cp) {
8396 argv++;
8397 break;
8398 }
8399 do {
8400 switch (c) {
8401 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008402 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008403 break;
8404 default:
8405 /* run 'typecmd' for other options */
8406 return 0;
8407 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008408 c = *cp++;
8409 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008410 }
8411 return argv;
8412}
8413#endif
8414
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008415/*
8416 * Make a variable a local variable. When a variable is made local, it's
8417 * value and flags are saved in a localvar structure. The saved values
8418 * will be restored when the shell function returns. We handle the name
8419 * "-" as a special case.
8420 */
8421static void
8422mklocal(char *name)
8423{
8424 struct localvar *lvp;
8425 struct var **vpp;
8426 struct var *vp;
8427
8428 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008429 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008430 if (LONE_DASH(name)) {
8431 char *p;
8432 p = ckmalloc(sizeof(optlist));
8433 lvp->text = memcpy(p, optlist, sizeof(optlist));
8434 vp = NULL;
8435 } else {
8436 char *eq;
8437
8438 vpp = hashvar(name);
8439 vp = *findvar(vpp, name);
8440 eq = strchr(name, '=');
8441 if (vp == NULL) {
8442 if (eq)
8443 setvareq(name, VSTRFIXED);
8444 else
8445 setvar(name, NULL, VSTRFIXED);
8446 vp = *vpp; /* the new variable */
8447 lvp->flags = VUNSET;
8448 } else {
8449 lvp->text = vp->text;
8450 lvp->flags = vp->flags;
8451 vp->flags |= VSTRFIXED|VTEXTFIXED;
8452 if (eq)
8453 setvareq(name, 0);
8454 }
8455 }
8456 lvp->vp = vp;
8457 lvp->next = localvars;
8458 localvars = lvp;
8459 INT_ON;
8460}
8461
8462/*
8463 * The "local" command.
8464 */
8465static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008466localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008467{
8468 char *name;
8469
8470 argv = argptr;
8471 while ((name = *argv++) != NULL) {
8472 mklocal(name);
8473 }
8474 return 0;
8475}
8476
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008477static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008478falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008479{
8480 return 1;
8481}
8482
8483static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008484truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008485{
8486 return 0;
8487}
8488
8489static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008490execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008491{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008492 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008493 iflag = 0; /* exit on error */
8494 mflag = 0;
8495 optschanged();
8496 shellexec(argv + 1, pathval(), 0);
8497 }
8498 return 0;
8499}
8500
8501/*
8502 * The return command.
8503 */
8504static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008505returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008506{
8507 /*
8508 * If called outside a function, do what ksh does;
8509 * skip the rest of the file.
8510 */
8511 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8512 return argv[1] ? number(argv[1]) : exitstatus;
8513}
8514
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008515/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008516static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008517static int dotcmd(int, char **);
8518static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008519static int exitcmd(int, char **);
8520static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008521#if ENABLE_ASH_GETOPTS
8522static int getoptscmd(int, char **);
8523#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008524#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008525static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008526#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008527#if ENABLE_ASH_MATH_SUPPORT
8528static int letcmd(int, char **);
8529#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008530static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008531static int setcmd(int, char **);
8532static int shiftcmd(int, char **);
8533static int timescmd(int, char **);
8534static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008535static int umaskcmd(int, char **);
8536static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008537static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008538
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008539#define BUILTIN_NOSPEC "0"
8540#define BUILTIN_SPECIAL "1"
8541#define BUILTIN_REGULAR "2"
8542#define BUILTIN_SPEC_REG "3"
8543#define BUILTIN_ASSIGN "4"
8544#define BUILTIN_SPEC_ASSG "5"
8545#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008546#define BUILTIN_SPEC_REG_ASSG "7"
8547
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008548/* We do not handle [[ expr ]] bashism bash-compatibly,
8549 * we make it a synonym of [ expr ].
8550 * Basically, word splitting and pathname expansion should NOT be performed
8551 * Examples:
8552 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8553 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8554 * Additional operators:
8555 * || and && should work as -o and -a
8556 * =~ regexp match
8557 * Apart from the above, [[ expr ]] should work as [ expr ]
8558 */
8559
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008560#define echocmd echo_main
8561#define printfcmd printf_main
8562#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008563
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008564/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008565static const struct builtincmd builtintab[] = {
8566 { BUILTIN_SPEC_REG ".", dotcmd },
8567 { BUILTIN_SPEC_REG ":", truecmd },
8568#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008569 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008570#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008571 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008572#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008573#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008574#if ENABLE_ASH_ALIAS
8575 { BUILTIN_REG_ASSG "alias", aliascmd },
8576#endif
8577#if JOBS
8578 { BUILTIN_REGULAR "bg", fg_bgcmd },
8579#endif
8580 { BUILTIN_SPEC_REG "break", breakcmd },
8581 { BUILTIN_REGULAR "cd", cdcmd },
8582 { BUILTIN_NOSPEC "chdir", cdcmd },
8583#if ENABLE_ASH_CMDCMD
8584 { BUILTIN_REGULAR "command", commandcmd },
8585#endif
8586 { BUILTIN_SPEC_REG "continue", breakcmd },
8587#if ENABLE_ASH_BUILTIN_ECHO
8588 { BUILTIN_REGULAR "echo", echocmd },
8589#endif
8590 { BUILTIN_SPEC_REG "eval", evalcmd },
8591 { BUILTIN_SPEC_REG "exec", execcmd },
8592 { BUILTIN_SPEC_REG "exit", exitcmd },
8593 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8594 { BUILTIN_REGULAR "false", falsecmd },
8595#if JOBS
8596 { BUILTIN_REGULAR "fg", fg_bgcmd },
8597#endif
8598#if ENABLE_ASH_GETOPTS
8599 { BUILTIN_REGULAR "getopts", getoptscmd },
8600#endif
8601 { BUILTIN_NOSPEC "hash", hashcmd },
8602#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8603 { BUILTIN_NOSPEC "help", helpcmd },
8604#endif
8605#if JOBS
8606 { BUILTIN_REGULAR "jobs", jobscmd },
8607 { BUILTIN_REGULAR "kill", killcmd },
8608#endif
8609#if ENABLE_ASH_MATH_SUPPORT
8610 { BUILTIN_NOSPEC "let", letcmd },
8611#endif
8612 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008613#if ENABLE_ASH_BUILTIN_PRINTF
8614 { BUILTIN_REGULAR "printf", printfcmd },
8615#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008616 { BUILTIN_NOSPEC "pwd", pwdcmd },
8617 { BUILTIN_REGULAR "read", readcmd },
8618 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8619 { BUILTIN_SPEC_REG "return", returncmd },
8620 { BUILTIN_SPEC_REG "set", setcmd },
8621 { BUILTIN_SPEC_REG "shift", shiftcmd },
8622 { BUILTIN_SPEC_REG "source", dotcmd },
8623#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008624 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008625#endif
8626 { BUILTIN_SPEC_REG "times", timescmd },
8627 { BUILTIN_SPEC_REG "trap", trapcmd },
8628 { BUILTIN_REGULAR "true", truecmd },
8629 { BUILTIN_NOSPEC "type", typecmd },
8630 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8631 { BUILTIN_REGULAR "umask", umaskcmd },
8632#if ENABLE_ASH_ALIAS
8633 { BUILTIN_REGULAR "unalias", unaliascmd },
8634#endif
8635 { BUILTIN_SPEC_REG "unset", unsetcmd },
8636 { BUILTIN_REGULAR "wait", waitcmd },
8637};
8638
Denis Vlasenko80591b02008-03-25 07:49:43 +00008639/* Should match the above table! */
8640#define COMMANDCMD (builtintab + \
8641 2 + \
8642 1 * ENABLE_ASH_BUILTIN_TEST + \
8643 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8644 1 * ENABLE_ASH_ALIAS + \
8645 1 * ENABLE_ASH_JOB_CONTROL + \
8646 3)
8647#define EXECCMD (builtintab + \
8648 2 + \
8649 1 * ENABLE_ASH_BUILTIN_TEST + \
8650 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8651 1 * ENABLE_ASH_ALIAS + \
8652 1 * ENABLE_ASH_JOB_CONTROL + \
8653 3 + \
8654 1 * ENABLE_ASH_CMDCMD + \
8655 1 + \
8656 ENABLE_ASH_BUILTIN_ECHO + \
8657 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008658
8659/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008660 * Search the table of builtin commands.
8661 */
8662static struct builtincmd *
8663find_builtin(const char *name)
8664{
8665 struct builtincmd *bp;
8666
8667 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008668 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008669 pstrcmp
8670 );
8671 return bp;
8672}
8673
8674/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008675 * Execute a simple command.
8676 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008677static int
8678isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008679{
8680 const char *q = endofname(p);
8681 if (p == q)
8682 return 0;
8683 return *q == '=';
8684}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008685static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008686bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008687{
8688 /* Preserve exitstatus of a previous possible redirection
8689 * as POSIX mandates */
8690 return back_exitstatus;
8691}
Eric Andersenc470f442003-07-28 09:56:35 +00008692static void
8693evalcommand(union node *cmd, int flags)
8694{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008695 static const struct builtincmd null_bltin = {
8696 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008697 };
Eric Andersenc470f442003-07-28 09:56:35 +00008698 struct stackmark smark;
8699 union node *argp;
8700 struct arglist arglist;
8701 struct arglist varlist;
8702 char **argv;
8703 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008704 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008705 struct cmdentry cmdentry;
8706 struct job *jp;
8707 char *lastarg;
8708 const char *path;
8709 int spclbltin;
8710 int cmd_is_exec;
8711 int status;
8712 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008713 struct builtincmd *bcmd;
8714 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008715
8716 /* First expand the arguments. */
8717 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8718 setstackmark(&smark);
8719 back_exitstatus = 0;
8720
8721 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008722 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008723 varlist.lastp = &varlist.list;
8724 *varlist.lastp = NULL;
8725 arglist.lastp = &arglist.list;
8726 *arglist.lastp = NULL;
8727
8728 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008729 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008730 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8731 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8732 }
8733
Eric Andersenc470f442003-07-28 09:56:35 +00008734 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8735 struct strlist **spp;
8736
8737 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008738 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008739 expandarg(argp, &arglist, EXP_VARTILDE);
8740 else
8741 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8742
Eric Andersenc470f442003-07-28 09:56:35 +00008743 for (sp = *spp; sp; sp = sp->next)
8744 argc++;
8745 }
8746
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008747 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008748 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008749 TRACE(("evalcommand arg: %s\n", sp->text));
8750 *nargv++ = sp->text;
8751 }
8752 *nargv = NULL;
8753
8754 lastarg = NULL;
8755 if (iflag && funcnest == 0 && argc > 0)
8756 lastarg = nargv[-1];
8757
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008758 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008759 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008760 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008761
8762 path = vpath.text;
8763 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8764 struct strlist **spp;
8765 char *p;
8766
8767 spp = varlist.lastp;
8768 expandarg(argp, &varlist, EXP_VARTILDE);
8769
8770 /*
8771 * Modify the command lookup path, if a PATH= assignment
8772 * is present
8773 */
8774 p = (*spp)->text;
8775 if (varequal(p, path))
8776 path = p;
8777 }
8778
8779 /* Print the command if xflag is set. */
8780 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008781 int n;
8782 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008783
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008784 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008785 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008786
8787 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008788 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008789 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008790 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008791 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008792 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008793 p--;
8794 }
8795 }
8796 sp = arglist.list;
8797 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008798 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008799 }
8800
8801 cmd_is_exec = 0;
8802 spclbltin = -1;
8803
8804 /* Now locate the command. */
8805 if (argc) {
8806 const char *oldpath;
8807 int cmd_flag = DO_ERR;
8808
8809 path += 5;
8810 oldpath = path;
8811 for (;;) {
8812 find_command(argv[0], &cmdentry, cmd_flag, path);
8813 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008814 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008815 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008816 goto bail;
8817 }
8818
8819 /* implement bltin and command here */
8820 if (cmdentry.cmdtype != CMDBUILTIN)
8821 break;
8822 if (spclbltin < 0)
8823 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8824 if (cmdentry.u.cmd == EXECCMD)
8825 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008826#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008827 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008828 path = oldpath;
8829 nargv = parse_command_args(argv, &path);
8830 if (!nargv)
8831 break;
8832 argc -= nargv - argv;
8833 argv = nargv;
8834 cmd_flag |= DO_NOFUNC;
8835 } else
8836#endif
8837 break;
8838 }
8839 }
8840
8841 if (status) {
8842 /* We have a redirection error. */
8843 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008844 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008845 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008846 exitstatus = status;
8847 goto out;
8848 }
8849
8850 /* Execute the command. */
8851 switch (cmdentry.cmdtype) {
8852 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008853#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008854 {
8855 /* find_command() encodes applet_no as (-2 - applet_no) */
8856 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008857 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008858 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008859 /* run <applet>_main() */
8860 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008861 break;
8862 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008863 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008864#endif
8865
Eric Andersenc470f442003-07-28 09:56:35 +00008866 /* Fork off a child process if necessary. */
8867 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008868 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008869 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008870 if (forkshell(jp, cmd, FORK_FG) != 0) {
8871 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008872 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008873 break;
8874 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008875 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008876 }
8877 listsetvar(varlist.list, VEXPORT|VSTACK);
8878 shellexec(argv, path, cmdentry.u.index);
8879 /* NOTREACHED */
8880
8881 case CMDBUILTIN:
8882 cmdenviron = varlist.list;
8883 if (cmdenviron) {
8884 struct strlist *list = cmdenviron;
8885 int i = VNOSET;
8886 if (spclbltin > 0 || argc == 0) {
8887 i = 0;
8888 if (cmd_is_exec && argc > 1)
8889 i = VEXPORT;
8890 }
8891 listsetvar(list, i);
8892 }
8893 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8894 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008895 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008896 if (i == EXEXIT)
8897 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008898 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008899 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008900 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008901 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008902 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008903 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008904 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008905 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008906 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008907 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008908 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008909 }
8910 break;
8911
8912 case CMDFUNCTION:
8913 listsetvar(varlist.list, 0);
8914 if (evalfun(cmdentry.u.func, argc, argv, flags))
8915 goto raise;
8916 break;
8917 }
8918
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008919 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008920 popredir(cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008921 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008922 /* dsl: I think this is intended to be used to support
8923 * '_' in 'vi' command mode during line editing...
8924 * However I implemented that within libedit itself.
8925 */
8926 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008927 }
Eric Andersenc470f442003-07-28 09:56:35 +00008928 popstackmark(&smark);
8929}
8930
8931static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008932evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8933{
Eric Andersenc470f442003-07-28 09:56:35 +00008934 char *volatile savecmdname;
8935 struct jmploc *volatile savehandler;
8936 struct jmploc jmploc;
8937 int i;
8938
8939 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008940 i = setjmp(jmploc.loc);
8941 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008942 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008943 savehandler = exception_handler;
8944 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008945 commandname = argv[0];
8946 argptr = argv + 1;
8947 optptr = NULL; /* initialize nextopt */
8948 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008949 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008950 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008951 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008952 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008953 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008954// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008955 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008956
8957 return i;
8958}
8959
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008960static int
8961goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008962{
8963 return !*endofname(p);
8964}
8965
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008966
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008967/*
8968 * Search for a command. This is called before we fork so that the
8969 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008970 * the child. The check for "goodname" is an overly conservative
8971 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008972 */
Eric Andersenc470f442003-07-28 09:56:35 +00008973static void
8974prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008975{
8976 struct cmdentry entry;
8977
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008978 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8979 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008980}
8981
Eric Andersencb57d552001-06-28 07:25:16 +00008982
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008983/* ============ Builtin commands
8984 *
8985 * Builtin commands whose functions are closely tied to evaluation
8986 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008987 */
8988
8989/*
Eric Andersencb57d552001-06-28 07:25:16 +00008990 * Handle break and continue commands. Break, continue, and return are
8991 * all handled by setting the evalskip flag. The evaluation routines
8992 * above all check this flag, and if it is set they start skipping
8993 * commands rather than executing them. The variable skipcount is
8994 * the number of loops to break/continue, or the number of function
8995 * levels to return. (The latter is always 1.) It should probably
8996 * be an error to break out of more loops than exist, but it isn't
8997 * in the standard shell so we don't make it one here.
8998 */
Eric Andersenc470f442003-07-28 09:56:35 +00008999static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009000breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009001{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009002 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009003
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009004 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009005 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009006 if (n > loopnest)
9007 n = loopnest;
9008 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009009 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009010 skipcount = n;
9011 }
9012 return 0;
9013}
9014
Eric Andersenc470f442003-07-28 09:56:35 +00009015
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009016/* ============ input.c
9017 *
Eric Andersen90898442003-08-06 11:20:52 +00009018 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009019 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009020
Eric Andersenc470f442003-07-28 09:56:35 +00009021#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009022
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009023enum {
9024 INPUT_PUSH_FILE = 1,
9025 INPUT_NOFILE_OK = 2,
9026};
Eric Andersencb57d552001-06-28 07:25:16 +00009027
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009028static int plinno = 1; /* input line number */
9029/* number of characters left in input buffer */
9030static int parsenleft; /* copy of parsefile->nleft */
9031static int parselleft; /* copy of parsefile->lleft */
9032/* next character in input buffer */
9033static char *parsenextc; /* copy of parsefile->nextc */
9034
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009035static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009036/* values of checkkwd variable */
9037#define CHKALIAS 0x1
9038#define CHKKWD 0x2
9039#define CHKNL 0x4
9040
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009041static void
9042popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009043{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009044 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009045
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009046 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009047#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009048 if (sp->ap) {
9049 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9050 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009051 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009052 if (sp->string != sp->ap->val) {
9053 free(sp->string);
9054 }
9055 sp->ap->flag &= ~ALIASINUSE;
9056 if (sp->ap->flag & ALIASDEAD) {
9057 unalias(sp->ap->name);
9058 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009059 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009060#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009061 parsenextc = sp->prevstring;
9062 parsenleft = sp->prevnleft;
9063/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009064 g_parsefile->strpush = sp->prev;
9065 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009066 free(sp);
9067 INT_ON;
9068}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009069
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009070static int
9071preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009072{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009073 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009074 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009075 parsenextc = buf;
9076
Denis Vlasenko38f63192007-01-22 09:03:07 +00009077#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009078 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009079 if (!iflag || g_parsefile->fd)
9080 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009081 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009082#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009083 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009084#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009085 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9086 if (nr == 0) {
9087 /* Ctrl+C pressed */
9088 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009089 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009090 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009091 raise(SIGINT);
9092 return 1;
9093 }
Eric Andersenc470f442003-07-28 09:56:35 +00009094 goto retry;
9095 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009096 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009097 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009098 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009099 }
Eric Andersencb57d552001-06-28 07:25:16 +00009100 }
9101#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009102 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009103#endif
9104
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009105#if 0
9106/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009107 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009108 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009109 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009110 if (flags >= 0 && (flags & O_NONBLOCK)) {
9111 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009112 if (fcntl(0, F_SETFL, flags) >= 0) {
9113 out2str("sh: turning off NDELAY mode\n");
9114 goto retry;
9115 }
9116 }
9117 }
9118 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009119#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009120 return nr;
9121}
9122
9123/*
9124 * Refill the input buffer and return the next input character:
9125 *
9126 * 1) If a string was pushed back on the input, pop it;
9127 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9128 * from a string so we can't refill the buffer, return EOF.
9129 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9130 * 4) Process input up to the next newline, deleting nul characters.
9131 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009132static int
Eric Andersenc470f442003-07-28 09:56:35 +00009133preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009134{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009135 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009136 int more;
9137 char savec;
9138
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009139 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009140#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009141 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009142 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009143 return PEOA;
9144 }
Eric Andersen2870d962001-07-02 17:27:21 +00009145#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009146 popstring();
9147 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009148 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009149 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009150 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009151 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009152 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009153
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009154 more = parselleft;
9155 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009156 again:
9157 more = preadfd();
9158 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009159 parselleft = parsenleft = EOF_NLEFT;
9160 return PEOF;
9161 }
9162 }
9163
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009164 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009165
9166 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009167 for (;;) {
9168 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009169
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009170 more--;
9171 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009172
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009173 if (!c)
9174 memmove(q, q + 1, more);
9175 else {
9176 q++;
9177 if (c == '\n') {
9178 parsenleft = q - parsenextc - 1;
9179 break;
9180 }
Eric Andersencb57d552001-06-28 07:25:16 +00009181 }
9182
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009183 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009184 parsenleft = q - parsenextc - 1;
9185 if (parsenleft < 0)
9186 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009187 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009188 }
9189 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009190 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009191
9192 savec = *q;
9193 *q = '\0';
9194
9195 if (vflag) {
9196 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009197 }
9198
9199 *q = savec;
9200
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009201 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009202}
9203
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009204#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009205static int
9206pgetc(void)
9207{
9208 return pgetc_as_macro();
9209}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009210
9211#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9212#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009213#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009214#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009215#endif
9216
9217/*
9218 * Same as pgetc(), but ignores PEOA.
9219 */
9220#if ENABLE_ASH_ALIAS
9221static int
9222pgetc2(void)
9223{
9224 int c;
9225
9226 do {
9227 c = pgetc_macro();
9228 } while (c == PEOA);
9229 return c;
9230}
9231#else
9232static int
9233pgetc2(void)
9234{
9235 return pgetc_macro();
9236}
9237#endif
9238
9239/*
9240 * Read a line from the script.
9241 */
9242static char *
9243pfgets(char *line, int len)
9244{
9245 char *p = line;
9246 int nleft = len;
9247 int c;
9248
9249 while (--nleft > 0) {
9250 c = pgetc2();
9251 if (c == PEOF) {
9252 if (p == line)
9253 return NULL;
9254 break;
9255 }
9256 *p++ = c;
9257 if (c == '\n')
9258 break;
9259 }
9260 *p = '\0';
9261 return line;
9262}
9263
Eric Andersenc470f442003-07-28 09:56:35 +00009264/*
9265 * Undo the last call to pgetc. Only one character may be pushed back.
9266 * PEOF may be pushed back.
9267 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009268static void
Eric Andersenc470f442003-07-28 09:56:35 +00009269pungetc(void)
9270{
9271 parsenleft++;
9272 parsenextc--;
9273}
Eric Andersencb57d552001-06-28 07:25:16 +00009274
9275/*
9276 * Push a string back onto the input at this current parsefile level.
9277 * We handle aliases this way.
9278 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009279#if !ENABLE_ASH_ALIAS
9280#define pushstring(s, ap) pushstring(s)
9281#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009282static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009283pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009284{
Eric Andersencb57d552001-06-28 07:25:16 +00009285 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009286 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009287
Eric Andersenc470f442003-07-28 09:56:35 +00009288 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009289 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009290/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009291 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009292 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009293 sp->prev = g_parsefile->strpush;
9294 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009295 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009296 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009297 sp->prevstring = parsenextc;
9298 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009299#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009300 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009301 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009302 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009303 sp->string = s;
9304 }
Eric Andersen2870d962001-07-02 17:27:21 +00009305#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009306 parsenextc = s;
9307 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009308 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009309}
9310
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009311/*
9312 * To handle the "." command, a stack of input files is used. Pushfile
9313 * adds a new entry to the stack and popfile restores the previous level.
9314 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009315static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009316pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009317{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009318 struct parsefile *pf;
9319
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009320 g_parsefile->nleft = parsenleft;
9321 g_parsefile->lleft = parselleft;
9322 g_parsefile->nextc = parsenextc;
9323 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009324 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009325 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009326 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009327 /*pf->strpush = NULL; - ckzalloc did it */
9328 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009329 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009330}
9331
9332static void
9333popfile(void)
9334{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009335 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009336
Denis Vlasenkob012b102007-02-19 22:43:01 +00009337 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009338 if (pf->fd >= 0)
9339 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009340 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009341 while (pf->strpush)
9342 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009343 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009344 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009345 parsenleft = g_parsefile->nleft;
9346 parselleft = g_parsefile->lleft;
9347 parsenextc = g_parsefile->nextc;
9348 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009349 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009350}
9351
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009352/*
9353 * Return to top level.
9354 */
9355static void
9356popallfiles(void)
9357{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009358 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009359 popfile();
9360}
9361
9362/*
9363 * Close the file(s) that the shell is reading commands from. Called
9364 * after a fork is done.
9365 */
9366static void
9367closescript(void)
9368{
9369 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009370 if (g_parsefile->fd > 0) {
9371 close(g_parsefile->fd);
9372 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009373 }
9374}
9375
9376/*
9377 * Like setinputfile, but takes an open file descriptor. Call this with
9378 * interrupts off.
9379 */
9380static void
9381setinputfd(int fd, int push)
9382{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009383 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009384 if (push) {
9385 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009386 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009387 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009388 g_parsefile->fd = fd;
9389 if (g_parsefile->buf == NULL)
9390 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009391 parselleft = parsenleft = 0;
9392 plinno = 1;
9393}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009394
Eric Andersenc470f442003-07-28 09:56:35 +00009395/*
9396 * Set the input to take input from a file. If push is set, push the
9397 * old input onto the stack first.
9398 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009399static int
9400setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009401{
9402 int fd;
9403 int fd2;
9404
Denis Vlasenkob012b102007-02-19 22:43:01 +00009405 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009406 fd = open(fname, O_RDONLY);
9407 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009408 if (flags & INPUT_NOFILE_OK)
9409 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009410 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009411 }
Eric Andersenc470f442003-07-28 09:56:35 +00009412 if (fd < 10) {
9413 fd2 = copyfd(fd, 10);
9414 close(fd);
9415 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009416 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009417 fd = fd2;
9418 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009419 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009420 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009421 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009422 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009423}
9424
Eric Andersencb57d552001-06-28 07:25:16 +00009425/*
9426 * Like setinputfile, but takes input from a string.
9427 */
Eric Andersenc470f442003-07-28 09:56:35 +00009428static void
9429setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009430{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009431 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009432 pushfile();
9433 parsenextc = string;
9434 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009435 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009436 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009437 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009438}
9439
9440
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009441/* ============ mail.c
9442 *
9443 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009444 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009445
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009446#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009447
Eric Andersencb57d552001-06-28 07:25:16 +00009448#define MAXMBOXES 10
9449
Eric Andersenc470f442003-07-28 09:56:35 +00009450/* times of mailboxes */
9451static time_t mailtime[MAXMBOXES];
9452/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009453static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009454
Eric Andersencb57d552001-06-28 07:25:16 +00009455/*
Eric Andersenc470f442003-07-28 09:56:35 +00009456 * Print appropriate message(s) if mail has arrived.
9457 * If mail_var_path_changed is set,
9458 * then the value of MAIL has mail_var_path_changed,
9459 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009460 */
Eric Andersenc470f442003-07-28 09:56:35 +00009461static void
9462chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009463{
Eric Andersencb57d552001-06-28 07:25:16 +00009464 const char *mpath;
9465 char *p;
9466 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009467 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009468 struct stackmark smark;
9469 struct stat statb;
9470
Eric Andersencb57d552001-06-28 07:25:16 +00009471 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009472 mpath = mpathset() ? mpathval() : mailval();
9473 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009474 p = padvance(&mpath, nullstr);
9475 if (p == NULL)
9476 break;
9477 if (*p == '\0')
9478 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009479 for (q = p; *q; q++)
9480 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009481#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009482 if (q[-1] != '/')
9483 abort();
9484#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009485 q[-1] = '\0'; /* delete trailing '/' */
9486 if (stat(p, &statb) < 0) {
9487 *mtp = 0;
9488 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009489 }
Eric Andersenc470f442003-07-28 09:56:35 +00009490 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9491 fprintf(
9492 stderr, snlfmt,
9493 pathopt ? pathopt : "you have mail"
9494 );
9495 }
9496 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009497 }
Eric Andersenc470f442003-07-28 09:56:35 +00009498 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009499 popstackmark(&smark);
9500}
Eric Andersencb57d552001-06-28 07:25:16 +00009501
Eric Andersenc470f442003-07-28 09:56:35 +00009502static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009503changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009504{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009505 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009506}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009507
Denis Vlasenko131ae172007-02-18 13:00:19 +00009508#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009509
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009510
9511/* ============ ??? */
9512
Eric Andersencb57d552001-06-28 07:25:16 +00009513/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009514 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009515 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009516static void
9517setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009518{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009519 char **newparam;
9520 char **ap;
9521 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009522
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009523 for (nparam = 0; argv[nparam]; nparam++)
9524 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009525 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9526 while (*argv) {
9527 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009528 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009529 *ap = NULL;
9530 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009531 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009532 shellparam.nparam = nparam;
9533 shellparam.p = newparam;
9534#if ENABLE_ASH_GETOPTS
9535 shellparam.optind = 1;
9536 shellparam.optoff = -1;
9537#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009538}
9539
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009540/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009541 * Process shell options. The global variable argptr contains a pointer
9542 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009543 *
9544 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9545 * For a non-interactive shell, an error condition encountered
9546 * by a special built-in ... shall cause the shell to write a diagnostic message
9547 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009548 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009549 * ...
9550 * Utility syntax error (option or operand error) Shall exit
9551 * ...
9552 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9553 * we see that bash does not do that (set "finishes" with error code 1 instead,
9554 * and shell continues), and people rely on this behavior!
9555 * Testcase:
9556 * set -o barfoo 2>/dev/null
9557 * echo $?
9558 *
9559 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009560 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009561static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009562plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009563{
9564 int i;
9565
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009566 if (name) {
9567 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009568 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009569 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009570 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009571 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009572 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009573 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009574 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009575 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009576 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009577 if (val) {
9578 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9579 } else {
9580 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9581 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009582 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009583 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009584}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009585static void
9586setoption(int flag, int val)
9587{
9588 int i;
9589
9590 for (i = 0; i < NOPTS; i++) {
9591 if (optletters(i) == flag) {
9592 optlist[i] = val;
9593 return;
9594 }
9595 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009596 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009597 /* NOTREACHED */
9598}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009599static int
Eric Andersenc470f442003-07-28 09:56:35 +00009600options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009601{
9602 char *p;
9603 int val;
9604 int c;
9605
9606 if (cmdline)
9607 minusc = NULL;
9608 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009609 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009610 if (c != '-' && c != '+')
9611 break;
9612 argptr++;
9613 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009614 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009615 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009616 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009617 if (!cmdline) {
9618 /* "-" means turn off -x and -v */
9619 if (p[0] == '\0')
9620 xflag = vflag = 0;
9621 /* "--" means reset params */
9622 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009623 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009624 }
Eric Andersenc470f442003-07-28 09:56:35 +00009625 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009626 }
Eric Andersencb57d552001-06-28 07:25:16 +00009627 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009628 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009629 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009630 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009631 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009632 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009633 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009634 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009635 /* it already printed err message */
9636 return 1; /* error */
9637 }
Eric Andersencb57d552001-06-28 07:25:16 +00009638 if (*argptr)
9639 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009640 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9641 isloginsh = 1;
9642 /* bash does not accept +-login, we also won't */
9643 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009644 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009645 isloginsh = 1;
9646 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009647 } else {
9648 setoption(c, val);
9649 }
9650 }
9651 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009652 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009653}
9654
Eric Andersencb57d552001-06-28 07:25:16 +00009655/*
Eric Andersencb57d552001-06-28 07:25:16 +00009656 * The shift builtin command.
9657 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009658static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009659shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009660{
9661 int n;
9662 char **ap1, **ap2;
9663
9664 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009665 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009666 n = number(argv[1]);
9667 if (n > shellparam.nparam)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +00009668 n = shellparam.nparam;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009669 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009670 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009671 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009672 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009673 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009674 }
9675 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009676 while ((*ap2++ = *ap1++) != NULL)
9677 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009678#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009679 shellparam.optind = 1;
9680 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009681#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009682 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009683 return 0;
9684}
9685
Eric Andersencb57d552001-06-28 07:25:16 +00009686/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009687 * POSIX requires that 'set' (but not export or readonly) output the
9688 * variables in lexicographic order - by the locale's collating order (sigh).
9689 * Maybe we could keep them in an ordered balanced binary tree
9690 * instead of hashed lists.
9691 * For now just roll 'em through qsort for printing...
9692 */
9693static int
9694showvars(const char *sep_prefix, int on, int off)
9695{
9696 const char *sep;
9697 char **ep, **epend;
9698
9699 ep = listvars(on, off, &epend);
9700 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9701
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009702 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009703
9704 for (; ep < epend; ep++) {
9705 const char *p;
9706 const char *q;
9707
9708 p = strchrnul(*ep, '=');
9709 q = nullstr;
9710 if (*p)
9711 q = single_quote(++p);
9712 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9713 }
9714 return 0;
9715}
9716
9717/*
Eric Andersencb57d552001-06-28 07:25:16 +00009718 * The set command builtin.
9719 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009720static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009721setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009722{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009723 int retval;
9724
Denis Vlasenko68404f12008-03-17 09:00:54 +00009725 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009726 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009727 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009728 retval = 1;
9729 if (!options(0)) { /* if no parse error... */
9730 retval = 0;
9731 optschanged();
9732 if (*argptr != NULL) {
9733 setparam(argptr);
9734 }
Eric Andersencb57d552001-06-28 07:25:16 +00009735 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009736 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009737 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009738}
9739
Denis Vlasenko131ae172007-02-18 13:00:19 +00009740#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009741static void
9742change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009743{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009744 /* Galois LFSR parameter */
9745 /* Taps at 32 31 29 1: */
9746 enum { MASK = 0x8000000b };
9747 /* Another example - taps at 32 31 30 10: */
9748 /* MASK = 0x00400007 */
9749
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009750 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009751 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009752 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009753
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009754 /* LCG has period of 2^32 and alternating lowest bit */
9755 random_LCG = 1664525 * random_LCG + 1013904223;
9756 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9757 t = (random_galois_LFSR << 1);
9758 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9759 t ^= MASK;
9760 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009761 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009762 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009763 * for $RANDOM range. Combining with subtraction is
9764 * just for fun. + and ^ would work equally well. */
9765 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009766 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009767 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009768 vrandom.flags &= ~VNOFUNC;
9769 } else {
9770 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009771 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009772 }
Eric Andersenef02f822004-03-11 13:34:24 +00009773}
Eric Andersen16767e22004-03-16 05:14:10 +00009774#endif
9775
Denis Vlasenko131ae172007-02-18 13:00:19 +00009776#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009777static int
Eric Andersenc470f442003-07-28 09:56:35 +00009778getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009779{
9780 char *p, *q;
9781 char c = '?';
9782 int done = 0;
9783 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009784 char s[12];
9785 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009786
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009787 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009788 return 1;
9789 optnext = optfirst + *param_optind - 1;
9790
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009791 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009792 p = NULL;
9793 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009794 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009795 if (p == NULL || *p == '\0') {
9796 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009797 p = *optnext;
9798 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009799 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009800 p = NULL;
9801 done = 1;
9802 goto out;
9803 }
9804 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009805 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009806 goto atend;
9807 }
9808
9809 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009810 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009811 if (*q == '\0') {
9812 if (optstr[0] == ':') {
9813 s[0] = c;
9814 s[1] = '\0';
9815 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009816 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009817 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009818 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009819 }
9820 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009821 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009822 }
9823 if (*++q == ':')
9824 q++;
9825 }
9826
9827 if (*++q == ':') {
9828 if (*p == '\0' && (p = *optnext) == NULL) {
9829 if (optstr[0] == ':') {
9830 s[0] = c;
9831 s[1] = '\0';
9832 err |= setvarsafe("OPTARG", s, 0);
9833 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009834 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009835 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009836 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009837 c = '?';
9838 }
Eric Andersenc470f442003-07-28 09:56:35 +00009839 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009840 }
9841
9842 if (p == *optnext)
9843 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009844 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009845 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009846 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009847 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009848 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009849 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009850 *param_optind = optnext - optfirst + 1;
9851 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009852 err |= setvarsafe("OPTIND", s, VNOFUNC);
9853 s[0] = c;
9854 s[1] = '\0';
9855 err |= setvarsafe(optvar, s, 0);
9856 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009857 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009858 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009859 flush_stdout_stderr();
9860 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009861 }
9862 return done;
9863}
Eric Andersenc470f442003-07-28 09:56:35 +00009864
9865/*
9866 * The getopts builtin. Shellparam.optnext points to the next argument
9867 * to be processed. Shellparam.optptr points to the next character to
9868 * be processed in the current argument. If shellparam.optnext is NULL,
9869 * then it's the first time getopts has been called.
9870 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009871static int
Eric Andersenc470f442003-07-28 09:56:35 +00009872getoptscmd(int argc, char **argv)
9873{
9874 char **optbase;
9875
9876 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009877 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009878 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009879 optbase = shellparam.p;
9880 if (shellparam.optind > shellparam.nparam + 1) {
9881 shellparam.optind = 1;
9882 shellparam.optoff = -1;
9883 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009884 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009885 optbase = &argv[3];
9886 if (shellparam.optind > argc - 2) {
9887 shellparam.optind = 1;
9888 shellparam.optoff = -1;
9889 }
9890 }
9891
9892 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009893 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009894}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009895#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009896
Eric Andersencb57d552001-06-28 07:25:16 +00009897
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009898/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009899
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009900struct heredoc {
9901 struct heredoc *next; /* next here document in list */
9902 union node *here; /* redirection node */
9903 char *eofmark; /* string indicating end of input */
9904 smallint striptabs; /* if set, strip leading tabs */
9905};
9906
9907static smallint tokpushback; /* last token pushed back */
9908static smallint parsebackquote; /* nonzero if we are inside backquotes */
9909static smallint quoteflag; /* set if (part of) last token was quoted */
9910static token_id_t lasttoken; /* last token read (integer id Txxx) */
9911static struct heredoc *heredoclist; /* list of here documents to read */
9912static char *wordtext; /* text of last word returned by readtoken */
9913static struct nodelist *backquotelist;
9914static union node *redirnode;
9915static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009916/*
9917 * NEOF is returned by parsecmd when it encounters an end of file. It
9918 * must be distinct from NULL, so we use the address of a variable that
9919 * happens to be handy.
9920 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009921#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009922
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009923static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009924static void
9925raise_error_syntax(const char *msg)
9926{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009927 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009928 /* NOTREACHED */
9929}
9930
9931/*
9932 * Called when an unexpected token is read during the parse. The argument
9933 * is the token that is expected, or -1 if more than one type of token can
9934 * occur at this point.
9935 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009936static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009937static void
9938raise_error_unexpected_syntax(int token)
9939{
9940 char msg[64];
9941 int l;
9942
9943 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9944 if (token >= 0)
9945 sprintf(msg + l, " (expecting %s)", tokname(token));
9946 raise_error_syntax(msg);
9947 /* NOTREACHED */
9948}
Eric Andersencb57d552001-06-28 07:25:16 +00009949
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009950#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009951
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009952/* parsing is heavily cross-recursive, need these forward decls */
9953static union node *andor(void);
9954static union node *pipeline(void);
9955static union node *parse_command(void);
9956static void parseheredoc(void);
9957static char peektoken(void);
9958static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009959
Eric Andersenc470f442003-07-28 09:56:35 +00009960static union node *
9961list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009962{
9963 union node *n1, *n2, *n3;
9964 int tok;
9965
Eric Andersenc470f442003-07-28 09:56:35 +00009966 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9967 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009968 return NULL;
9969 n1 = NULL;
9970 for (;;) {
9971 n2 = andor();
9972 tok = readtoken();
9973 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009974 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009975 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009976 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009977 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009978 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009979 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009980 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009981 n2 = n3;
9982 }
9983 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009984 }
9985 }
9986 if (n1 == NULL) {
9987 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009988 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009989 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009990 n3->type = NSEMI;
9991 n3->nbinary.ch1 = n1;
9992 n3->nbinary.ch2 = n2;
9993 n1 = n3;
9994 }
9995 switch (tok) {
9996 case TBACKGND:
9997 case TSEMI:
9998 tok = readtoken();
9999 /* fall through */
10000 case TNL:
10001 if (tok == TNL) {
10002 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010003 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010004 return n1;
10005 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010006 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010007 }
Eric Andersenc470f442003-07-28 09:56:35 +000010008 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010009 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010010 return n1;
10011 break;
10012 case TEOF:
10013 if (heredoclist)
10014 parseheredoc();
10015 else
Eric Andersenc470f442003-07-28 09:56:35 +000010016 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010017 return n1;
10018 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010019 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010020 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010021 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010022 return n1;
10023 }
10024 }
10025}
10026
Eric Andersenc470f442003-07-28 09:56:35 +000010027static union node *
10028andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010029{
Eric Andersencb57d552001-06-28 07:25:16 +000010030 union node *n1, *n2, *n3;
10031 int t;
10032
Eric Andersencb57d552001-06-28 07:25:16 +000010033 n1 = pipeline();
10034 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010035 t = readtoken();
10036 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010037 t = NAND;
10038 } else if (t == TOR) {
10039 t = NOR;
10040 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010041 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010042 return n1;
10043 }
Eric Andersenc470f442003-07-28 09:56:35 +000010044 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010045 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010046 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010047 n3->type = t;
10048 n3->nbinary.ch1 = n1;
10049 n3->nbinary.ch2 = n2;
10050 n1 = n3;
10051 }
10052}
10053
Eric Andersenc470f442003-07-28 09:56:35 +000010054static union node *
10055pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010056{
Eric Andersencb57d552001-06-28 07:25:16 +000010057 union node *n1, *n2, *pipenode;
10058 struct nodelist *lp, *prev;
10059 int negate;
10060
10061 negate = 0;
10062 TRACE(("pipeline: entered\n"));
10063 if (readtoken() == TNOT) {
10064 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010065 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010066 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010067 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010068 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010069 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010070 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010071 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010072 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010073 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010074 pipenode->npipe.cmdlist = lp;
10075 lp->n = n1;
10076 do {
10077 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010078 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010079 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010080 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010081 prev->next = lp;
10082 } while (readtoken() == TPIPE);
10083 lp->next = NULL;
10084 n1 = pipenode;
10085 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010086 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010087 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010088 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010089 n2->type = NNOT;
10090 n2->nnot.com = n1;
10091 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010092 }
10093 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010094}
10095
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010096static union node *
10097makename(void)
10098{
10099 union node *n;
10100
Denis Vlasenko597906c2008-02-20 16:38:54 +000010101 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010102 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010103 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010104 n->narg.text = wordtext;
10105 n->narg.backquote = backquotelist;
10106 return n;
10107}
10108
10109static void
10110fixredir(union node *n, const char *text, int err)
10111{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010112 int fd;
10113
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010114 TRACE(("Fix redir %s %d\n", text, err));
10115 if (!err)
10116 n->ndup.vname = NULL;
10117
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010118 fd = bb_strtou(text, NULL, 10);
10119 if (!errno && fd >= 0)
10120 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010121 else if (LONE_DASH(text))
10122 n->ndup.dupfd = -1;
10123 else {
10124 if (err)
10125 raise_error_syntax("Bad fd number");
10126 n->ndup.vname = makename();
10127 }
10128}
10129
10130/*
10131 * Returns true if the text contains nothing to expand (no dollar signs
10132 * or backquotes).
10133 */
10134static int
10135noexpand(char *text)
10136{
10137 char *p;
10138 char c;
10139
10140 p = text;
10141 while ((c = *p++) != '\0') {
10142 if (c == CTLQUOTEMARK)
10143 continue;
10144 if (c == CTLESC)
10145 p++;
10146 else if (SIT(c, BASESYNTAX) == CCTL)
10147 return 0;
10148 }
10149 return 1;
10150}
10151
10152static void
10153parsefname(void)
10154{
10155 union node *n = redirnode;
10156
10157 if (readtoken() != TWORD)
10158 raise_error_unexpected_syntax(-1);
10159 if (n->type == NHERE) {
10160 struct heredoc *here = heredoc;
10161 struct heredoc *p;
10162 int i;
10163
10164 if (quoteflag == 0)
10165 n->type = NXHERE;
10166 TRACE(("Here document %d\n", n->type));
10167 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10168 raise_error_syntax("Illegal eof marker for << redirection");
10169 rmescapes(wordtext);
10170 here->eofmark = wordtext;
10171 here->next = NULL;
10172 if (heredoclist == NULL)
10173 heredoclist = here;
10174 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010175 for (p = heredoclist; p->next; p = p->next)
10176 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010177 p->next = here;
10178 }
10179 } else if (n->type == NTOFD || n->type == NFROMFD) {
10180 fixredir(n, wordtext, 0);
10181 } else {
10182 n->nfile.fname = makename();
10183 }
10184}
Eric Andersencb57d552001-06-28 07:25:16 +000010185
Eric Andersenc470f442003-07-28 09:56:35 +000010186static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010187simplecmd(void)
10188{
10189 union node *args, **app;
10190 union node *n = NULL;
10191 union node *vars, **vpp;
10192 union node **rpp, *redir;
10193 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010194#if ENABLE_ASH_BASH_COMPAT
10195 smallint double_brackets_flag = 0;
10196#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010197
10198 args = NULL;
10199 app = &args;
10200 vars = NULL;
10201 vpp = &vars;
10202 redir = NULL;
10203 rpp = &redir;
10204
10205 savecheckkwd = CHKALIAS;
10206 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010207 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010208 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010209 t = readtoken();
10210 switch (t) {
10211#if ENABLE_ASH_BASH_COMPAT
10212 case TAND: /* "&&" */
10213 case TOR: /* "||" */
10214 if (!double_brackets_flag) {
10215 tokpushback = 1;
10216 goto out;
10217 }
10218 wordtext = (char *) (t == TAND ? "-a" : "-o");
10219#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010220 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010221 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010222 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010223 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010224 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010225#if ENABLE_ASH_BASH_COMPAT
10226 if (strcmp("[[", wordtext) == 0)
10227 double_brackets_flag = 1;
10228 else if (strcmp("]]", wordtext) == 0)
10229 double_brackets_flag = 0;
10230#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010231 n->narg.backquote = backquotelist;
10232 if (savecheckkwd && isassignment(wordtext)) {
10233 *vpp = n;
10234 vpp = &n->narg.next;
10235 } else {
10236 *app = n;
10237 app = &n->narg.next;
10238 savecheckkwd = 0;
10239 }
10240 break;
10241 case TREDIR:
10242 *rpp = n = redirnode;
10243 rpp = &n->nfile.next;
10244 parsefname(); /* read name of redirection file */
10245 break;
10246 case TLP:
10247 if (args && app == &args->narg.next
10248 && !vars && !redir
10249 ) {
10250 struct builtincmd *bcmd;
10251 const char *name;
10252
10253 /* We have a function */
10254 if (readtoken() != TRP)
10255 raise_error_unexpected_syntax(TRP);
10256 name = n->narg.text;
10257 if (!goodname(name)
10258 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10259 ) {
10260 raise_error_syntax("Bad function name");
10261 }
10262 n->type = NDEFUN;
10263 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10264 n->narg.next = parse_command();
10265 return n;
10266 }
10267 /* fall through */
10268 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010269 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010270 goto out;
10271 }
10272 }
10273 out:
10274 *app = NULL;
10275 *vpp = NULL;
10276 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010277 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010278 n->type = NCMD;
10279 n->ncmd.args = args;
10280 n->ncmd.assign = vars;
10281 n->ncmd.redirect = redir;
10282 return n;
10283}
10284
10285static union node *
10286parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010287{
Eric Andersencb57d552001-06-28 07:25:16 +000010288 union node *n1, *n2;
10289 union node *ap, **app;
10290 union node *cp, **cpp;
10291 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010292 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010293 int t;
10294
10295 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010296 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010297
Eric Andersencb57d552001-06-28 07:25:16 +000010298 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010299 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010300 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010301 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010302 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010303 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010304 n1->type = NIF;
10305 n1->nif.test = list(0);
10306 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010307 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010308 n1->nif.ifpart = list(0);
10309 n2 = n1;
10310 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010311 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010312 n2 = n2->nif.elsepart;
10313 n2->type = NIF;
10314 n2->nif.test = list(0);
10315 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010316 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010317 n2->nif.ifpart = list(0);
10318 }
10319 if (lasttoken == TELSE)
10320 n2->nif.elsepart = list(0);
10321 else {
10322 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010323 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010324 }
Eric Andersenc470f442003-07-28 09:56:35 +000010325 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010326 break;
10327 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010328 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010329 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010330 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010331 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010332 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010333 got = readtoken();
10334 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010335 TRACE(("expecting DO got %s %s\n", tokname(got),
10336 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010337 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010338 }
10339 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010340 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010341 break;
10342 }
10343 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010344 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010345 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010346 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010347 n1->type = NFOR;
10348 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010349 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010350 if (readtoken() == TIN) {
10351 app = &ap;
10352 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010353 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010354 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010355 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010356 n2->narg.text = wordtext;
10357 n2->narg.backquote = backquotelist;
10358 *app = n2;
10359 app = &n2->narg.next;
10360 }
10361 *app = NULL;
10362 n1->nfor.args = ap;
10363 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010364 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010365 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010366 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010367 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010368 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010369 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010370 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010371 n1->nfor.args = n2;
10372 /*
10373 * Newline or semicolon here is optional (but note
10374 * that the original Bourne shell only allowed NL).
10375 */
10376 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010377 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010378 }
Eric Andersenc470f442003-07-28 09:56:35 +000010379 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010380 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010381 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010382 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010383 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010384 break;
10385 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010386 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010387 n1->type = NCASE;
10388 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010389 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010390 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010391 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010392 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010393 n2->narg.text = wordtext;
10394 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010395 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010396 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010397 } while (readtoken() == TNL);
10398 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010399 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010400 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010401 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010402 checkkwd = CHKNL | CHKKWD;
10403 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010404 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010405 if (lasttoken == TLP)
10406 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010407 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010408 cp->type = NCLIST;
10409 app = &cp->nclist.pattern;
10410 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010411 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010412 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010413 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010414 ap->narg.text = wordtext;
10415 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010416 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010417 break;
10418 app = &ap->narg.next;
10419 readtoken();
10420 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010421 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010422 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010423 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010424 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010425
Eric Andersenc470f442003-07-28 09:56:35 +000010426 cpp = &cp->nclist.next;
10427
10428 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010429 t = readtoken();
10430 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010431 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010432 raise_error_unexpected_syntax(TENDCASE);
10433 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010434 }
Eric Andersenc470f442003-07-28 09:56:35 +000010435 }
Eric Andersencb57d552001-06-28 07:25:16 +000010436 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010437 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010438 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010439 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010440 n1->type = NSUBSHELL;
10441 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010442 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010443 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010444 break;
10445 case TBEGIN:
10446 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010447 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010448 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010449 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010450 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010451 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010452 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010453 }
10454
Eric Andersenc470f442003-07-28 09:56:35 +000010455 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010456 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010457
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010458 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010459 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010460 checkkwd = CHKKWD | CHKALIAS;
10461 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010462 while (readtoken() == TREDIR) {
10463 *rpp = n2 = redirnode;
10464 rpp = &n2->nfile.next;
10465 parsefname();
10466 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010467 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010468 *rpp = NULL;
10469 if (redir) {
10470 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010471 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010472 n2->type = NREDIR;
10473 n2->nredir.n = n1;
10474 n1 = n2;
10475 }
10476 n1->nredir.redirect = redir;
10477 }
Eric Andersencb57d552001-06-28 07:25:16 +000010478 return n1;
10479}
10480
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010481#if ENABLE_ASH_BASH_COMPAT
10482static int decode_dollar_squote(void)
10483{
10484 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10485 int c, cnt;
10486 char *p;
10487 char buf[4];
10488
10489 c = pgetc();
10490 p = strchr(C_escapes, c);
10491 if (p) {
10492 buf[0] = c;
10493 p = buf;
10494 cnt = 3;
10495 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10496 do {
10497 c = pgetc();
10498 *++p = c;
10499 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10500 pungetc();
10501 } else if (c == 'x') { /* \xHH */
10502 do {
10503 c = pgetc();
10504 *++p = c;
10505 } while (isxdigit(c) && --cnt);
10506 pungetc();
10507 if (cnt == 3) { /* \x but next char is "bad" */
10508 c = 'x';
10509 goto unrecognized;
10510 }
10511 } else { /* simple seq like \\ or \t */
10512 p++;
10513 }
10514 *p = '\0';
10515 p = buf;
10516 c = bb_process_escape_sequence((void*)&p);
10517 } else { /* unrecognized "\z": print both chars unless ' or " */
10518 if (c != '\'' && c != '"') {
10519 unrecognized:
10520 c |= 0x100; /* "please encode \, then me" */
10521 }
10522 }
10523 return c;
10524}
10525#endif
10526
Eric Andersencb57d552001-06-28 07:25:16 +000010527/*
10528 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10529 * is not NULL, read a here document. In the latter case, eofmark is the
10530 * word which marks the end of the document and striptabs is true if
10531 * leading tabs should be stripped from the document. The argument firstc
10532 * is the first character of the input token or document.
10533 *
10534 * Because C does not have internal subroutines, I have simulated them
10535 * using goto's to implement the subroutine linkage. The following macros
10536 * will run code that appears at the end of readtoken1.
10537 */
Eric Andersen2870d962001-07-02 17:27:21 +000010538#define CHECKEND() {goto checkend; checkend_return:;}
10539#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10540#define PARSESUB() {goto parsesub; parsesub_return:;}
10541#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10542#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10543#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010544static int
Eric Andersenc470f442003-07-28 09:56:35 +000010545readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010546{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010547 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010548 int c = firstc;
10549 char *out;
10550 int len;
10551 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010552 struct nodelist *bqlist;
10553 smallint quotef;
10554 smallint dblquote;
10555 smallint oldstyle;
10556 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010557#if ENABLE_ASH_EXPAND_PRMT
10558 smallint pssyntax; /* we are expanding a prompt string */
10559#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010560 int varnest; /* levels of variables expansion */
10561 int arinest; /* levels of arithmetic expansion */
10562 int parenlevel; /* levels of parens in arithmetic */
10563 int dqvarnest; /* levels of variables expansion within double quotes */
10564
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010565 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10566
Eric Andersencb57d552001-06-28 07:25:16 +000010567#if __GNUC__
10568 /* Avoid longjmp clobbering */
10569 (void) &out;
10570 (void) &quotef;
10571 (void) &dblquote;
10572 (void) &varnest;
10573 (void) &arinest;
10574 (void) &parenlevel;
10575 (void) &dqvarnest;
10576 (void) &oldstyle;
10577 (void) &prevsyntax;
10578 (void) &syntax;
10579#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010580 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010581 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010582 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010583 oldstyle = 0;
10584 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010585#if ENABLE_ASH_EXPAND_PRMT
10586 pssyntax = (syntax == PSSYNTAX);
10587 if (pssyntax)
10588 syntax = DQSYNTAX;
10589#endif
10590 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010591 varnest = 0;
10592 arinest = 0;
10593 parenlevel = 0;
10594 dqvarnest = 0;
10595
10596 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010597 loop: { /* for each line, until end of word */
10598 CHECKEND(); /* set c to PEOF if at end of here document */
10599 for (;;) { /* until end of line or end of word */
10600 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010601 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010602 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010603 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010604 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010605 USTPUTC(c, out);
10606 plinno++;
10607 if (doprompt)
10608 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010609 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010610 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010611 case CWORD:
10612 USTPUTC(c, out);
10613 break;
10614 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010615 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010616 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010617#if ENABLE_ASH_BASH_COMPAT
10618 if (c == '\\' && bash_dollar_squote) {
10619 c = decode_dollar_squote();
10620 if (c & 0x100) {
10621 USTPUTC('\\', out);
10622 c = (unsigned char)c;
10623 }
10624 }
10625#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010626 USTPUTC(c, out);
10627 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010628 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010629 c = pgetc2();
10630 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010631 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010632 USTPUTC('\\', out);
10633 pungetc();
10634 } else if (c == '\n') {
10635 if (doprompt)
10636 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010637 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010638#if ENABLE_ASH_EXPAND_PRMT
10639 if (c == '$' && pssyntax) {
10640 USTPUTC(CTLESC, out);
10641 USTPUTC('\\', out);
10642 }
10643#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010644 if (dblquote && c != '\\'
10645 && c != '`' && c != '$'
10646 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010647 ) {
10648 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010649 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010650 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010651 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010652 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010653 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010654 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010655 }
10656 break;
10657 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010658 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010659 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010660 if (eofmark == NULL) {
10661 USTPUTC(CTLQUOTEMARK, out);
10662 }
Eric Andersencb57d552001-06-28 07:25:16 +000010663 break;
10664 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010665 syntax = DQSYNTAX;
10666 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010667 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010668 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010669 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010670 if (eofmark != NULL && arinest == 0
10671 && varnest == 0
10672 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010673 USTPUTC(c, out);
10674 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010675 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010676 syntax = BASESYNTAX;
10677 dblquote = 0;
10678 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010679 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010680 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010681 }
10682 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010683 case CVAR: /* '$' */
10684 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010685 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010686 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010687 if (varnest > 0) {
10688 varnest--;
10689 if (dqvarnest > 0) {
10690 dqvarnest--;
10691 }
10692 USTPUTC(CTLENDVAR, out);
10693 } else {
10694 USTPUTC(c, out);
10695 }
10696 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010697#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010698 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010699 parenlevel++;
10700 USTPUTC(c, out);
10701 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010702 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010703 if (parenlevel > 0) {
10704 USTPUTC(c, out);
10705 --parenlevel;
10706 } else {
10707 if (pgetc() == ')') {
10708 if (--arinest == 0) {
10709 USTPUTC(CTLENDARI, out);
10710 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010711 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010712 } else
10713 USTPUTC(')', out);
10714 } else {
10715 /*
10716 * unbalanced parens
10717 * (don't 2nd guess - no error)
10718 */
10719 pungetc();
10720 USTPUTC(')', out);
10721 }
10722 }
10723 break;
10724#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010725 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010726 PARSEBACKQOLD();
10727 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010728 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010729 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010730 case CIGN:
10731 break;
10732 default:
10733 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010734 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010735#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010736 if (c != PEOA)
10737#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010738 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010739
Eric Andersencb57d552001-06-28 07:25:16 +000010740 }
10741 c = pgetc_macro();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010742 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010743 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010744 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010745#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010746 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010747 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010748#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010749 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010750 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010751 if (varnest != 0) {
10752 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010753 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010754 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010755 }
10756 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010757 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010758 out = stackblock();
10759 if (eofmark == NULL) {
Denis Vlasenko5a867312008-07-24 19:46:38 +000010760 if ((c == '>' || c == '<') && quotef == 0) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010761 int maxlen = 9 + 1; /* max 9 digit fd#: 999999999 */
10762 char *np = out;
10763 while (--maxlen && isdigit(*np))
10764 np++;
10765 if (*np == '\0') {
10766 PARSEREDIR(); /* passed as params: out, c */
10767 lasttoken = TREDIR;
10768 return lasttoken;
10769 }
10770 /* else: non-number X seen, interpret it
10771 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010772 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010773 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010774 }
10775 quoteflag = quotef;
10776 backquotelist = bqlist;
10777 grabstackblock(len);
10778 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010779 lasttoken = TWORD;
10780 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010781/* end of readtoken routine */
10782
Eric Andersencb57d552001-06-28 07:25:16 +000010783/*
10784 * Check to see whether we are at the end of the here document. When this
10785 * is called, c is set to the first character of the next input line. If
10786 * we are at the end of the here document, this routine sets the c to PEOF.
10787 */
Eric Andersenc470f442003-07-28 09:56:35 +000010788checkend: {
10789 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010790#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010791 if (c == PEOA) {
10792 c = pgetc2();
10793 }
10794#endif
10795 if (striptabs) {
10796 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010797 c = pgetc2();
10798 }
Eric Andersenc470f442003-07-28 09:56:35 +000010799 }
10800 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010801 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010802 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010803
Eric Andersenc470f442003-07-28 09:56:35 +000010804 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010805 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10806 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010807 if (*p == '\n' && *q == '\0') {
10808 c = PEOF;
10809 plinno++;
10810 needprompt = doprompt;
10811 } else {
10812 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010813 }
10814 }
10815 }
10816 }
Eric Andersenc470f442003-07-28 09:56:35 +000010817 goto checkend_return;
10818}
Eric Andersencb57d552001-06-28 07:25:16 +000010819
Eric Andersencb57d552001-06-28 07:25:16 +000010820/*
10821 * Parse a redirection operator. The variable "out" points to a string
10822 * specifying the fd to be redirected. The variable "c" contains the
10823 * first character of the redirection operator.
10824 */
Eric Andersenc470f442003-07-28 09:56:35 +000010825parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010826 /* out is already checked to be a valid number or "" */
10827 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010828 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010829
Denis Vlasenko597906c2008-02-20 16:38:54 +000010830 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010831 if (c == '>') {
10832 np->nfile.fd = 1;
10833 c = pgetc();
10834 if (c == '>')
10835 np->type = NAPPEND;
10836 else if (c == '|')
10837 np->type = NCLOBBER;
10838 else if (c == '&')
10839 np->type = NTOFD;
10840 else {
10841 np->type = NTO;
10842 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010843 }
Eric Andersenc470f442003-07-28 09:56:35 +000010844 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010845 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010846 c = pgetc();
10847 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010848 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010849 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010850 np = stzalloc(sizeof(struct nhere));
10851 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010852 }
10853 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010854 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010855 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010856 c = pgetc();
10857 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010858 heredoc->striptabs = 1;
10859 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010860 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010861 pungetc();
10862 }
10863 break;
10864
10865 case '&':
10866 np->type = NFROMFD;
10867 break;
10868
10869 case '>':
10870 np->type = NFROMTO;
10871 break;
10872
10873 default:
10874 np->type = NFROM;
10875 pungetc();
10876 break;
10877 }
Eric Andersencb57d552001-06-28 07:25:16 +000010878 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010879 if (fd >= 0)
10880 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010881 redirnode = np;
10882 goto parseredir_return;
10883}
Eric Andersencb57d552001-06-28 07:25:16 +000010884
Eric Andersencb57d552001-06-28 07:25:16 +000010885/*
10886 * Parse a substitution. At this point, we have read the dollar sign
10887 * and nothing else.
10888 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010889
10890/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10891 * (assuming ascii char codes, as the original implementation did) */
10892#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010893 (((unsigned)(c) - 33 < 32) \
10894 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010895parsesub: {
10896 int subtype;
10897 int typeloc;
10898 int flags;
10899 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010900 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010901
Eric Andersenc470f442003-07-28 09:56:35 +000010902 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010903 if (c <= PEOA_OR_PEOF
10904 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010905 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010906#if ENABLE_ASH_BASH_COMPAT
10907 if (c == '\'')
10908 bash_dollar_squote = 1;
10909 else
10910#endif
10911 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010912 pungetc();
10913 } else if (c == '(') { /* $(command) or $((arith)) */
10914 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010915#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010916 PARSEARITH();
10917#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010918 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010919#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010920 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010921 pungetc();
10922 PARSEBACKQNEW();
10923 }
10924 } else {
10925 USTPUTC(CTLVAR, out);
10926 typeloc = out - (char *)stackblock();
10927 USTPUTC(VSNORMAL, out);
10928 subtype = VSNORMAL;
10929 if (c == '{') {
10930 c = pgetc();
10931 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010932 c = pgetc();
10933 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010934 c = '#';
10935 else
10936 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010937 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010938 subtype = 0;
10939 }
10940 if (c > PEOA_OR_PEOF && is_name(c)) {
10941 do {
10942 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010943 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010944 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010945 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010946 do {
10947 STPUTC(c, out);
10948 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010949 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010950 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010951 USTPUTC(c, out);
10952 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010953 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010954 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010955
Eric Andersenc470f442003-07-28 09:56:35 +000010956 STPUTC('=', out);
10957 flags = 0;
10958 if (subtype == 0) {
10959 switch (c) {
10960 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010961 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010962#if ENABLE_ASH_BASH_COMPAT
10963 if (c == ':' || c == '$' || isdigit(c)) {
10964 pungetc();
10965 subtype = VSSUBSTR;
10966 break;
10967 }
10968#endif
10969 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010970 /*FALLTHROUGH*/
10971 default:
10972 p = strchr(types, c);
10973 if (p == NULL)
10974 goto badsub;
10975 subtype = p - types + VSNORMAL;
10976 break;
10977 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010978 case '#': {
10979 int cc = c;
10980 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10981 c = pgetc();
10982 if (c == cc)
10983 subtype++;
10984 else
10985 pungetc();
10986 break;
10987 }
10988#if ENABLE_ASH_BASH_COMPAT
10989 case '/':
10990 subtype = VSREPLACE;
10991 c = pgetc();
10992 if (c == '/')
10993 subtype++; /* VSREPLACEALL */
10994 else
10995 pungetc();
10996 break;
10997#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010998 }
Eric Andersenc470f442003-07-28 09:56:35 +000010999 } else {
11000 pungetc();
11001 }
11002 if (dblquote || arinest)
11003 flags |= VSQUOTE;
11004 *((char *)stackblock() + typeloc) = subtype | flags;
11005 if (subtype != VSNORMAL) {
11006 varnest++;
11007 if (dblquote || arinest) {
11008 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011009 }
11010 }
11011 }
Eric Andersenc470f442003-07-28 09:56:35 +000011012 goto parsesub_return;
11013}
Eric Andersencb57d552001-06-28 07:25:16 +000011014
Eric Andersencb57d552001-06-28 07:25:16 +000011015/*
11016 * Called to parse command substitutions. Newstyle is set if the command
11017 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11018 * list of commands (passed by reference), and savelen is the number of
11019 * characters on the top of the stack which must be preserved.
11020 */
Eric Andersenc470f442003-07-28 09:56:35 +000011021parsebackq: {
11022 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011023 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011024 union node *n;
11025 char *volatile str;
11026 struct jmploc jmploc;
11027 struct jmploc *volatile savehandler;
11028 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011029 smallint saveprompt = 0;
11030
Eric Andersencb57d552001-06-28 07:25:16 +000011031#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011032 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011033#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011034 savepbq = parsebackquote;
11035 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011036 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011037 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011038 exception_handler = savehandler;
11039 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011040 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011041 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011042 str = NULL;
11043 savelen = out - (char *)stackblock();
11044 if (savelen > 0) {
11045 str = ckmalloc(savelen);
11046 memcpy(str, stackblock(), savelen);
11047 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011048 savehandler = exception_handler;
11049 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011050 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011051 if (oldstyle) {
11052 /* We must read until the closing backquote, giving special
11053 treatment to some slashes, and then push the string and
11054 reread it as input, interpreting it normally. */
11055 char *pout;
11056 int pc;
11057 size_t psavelen;
11058 char *pstr;
11059
11060
11061 STARTSTACKSTR(pout);
11062 for (;;) {
11063 if (needprompt) {
11064 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011065 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011066 pc = pgetc();
11067 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011068 case '`':
11069 goto done;
11070
11071 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011072 pc = pgetc();
11073 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000011074 plinno++;
11075 if (doprompt)
11076 setprompt(2);
11077 /*
11078 * If eating a newline, avoid putting
11079 * the newline into the new character
11080 * stream (via the STPUTC after the
11081 * switch).
11082 */
11083 continue;
11084 }
11085 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011086 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011087 STPUTC('\\', pout);
11088 if (pc > PEOA_OR_PEOF) {
11089 break;
11090 }
11091 /* fall through */
11092
11093 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011094#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011095 case PEOA:
11096#endif
11097 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011098 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011099
11100 case '\n':
11101 plinno++;
11102 needprompt = doprompt;
11103 break;
11104
11105 default:
11106 break;
11107 }
11108 STPUTC(pc, pout);
11109 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011110 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011111 STPUTC('\0', pout);
11112 psavelen = pout - (char *)stackblock();
11113 if (psavelen > 0) {
11114 pstr = grabstackstr(pout);
11115 setinputstring(pstr);
11116 }
11117 }
11118 nlpp = &bqlist;
11119 while (*nlpp)
11120 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011121 *nlpp = stzalloc(sizeof(**nlpp));
11122 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011123 parsebackquote = oldstyle;
11124
11125 if (oldstyle) {
11126 saveprompt = doprompt;
11127 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011128 }
11129
Eric Andersenc470f442003-07-28 09:56:35 +000011130 n = list(2);
11131
11132 if (oldstyle)
11133 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011134 else if (readtoken() != TRP)
11135 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011136
11137 (*nlpp)->n = n;
11138 if (oldstyle) {
11139 /*
11140 * Start reading from old file again, ignoring any pushed back
11141 * tokens left from the backquote parsing
11142 */
11143 popfile();
11144 tokpushback = 0;
11145 }
11146 while (stackblocksize() <= savelen)
11147 growstackblock();
11148 STARTSTACKSTR(out);
11149 if (str) {
11150 memcpy(out, str, savelen);
11151 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011152 INT_OFF;
11153 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011154 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011155 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011156 }
11157 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011158 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011159 if (arinest || dblquote)
11160 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11161 else
11162 USTPUTC(CTLBACKQ, out);
11163 if (oldstyle)
11164 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011165 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011166}
11167
Denis Vlasenko131ae172007-02-18 13:00:19 +000011168#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011169/*
11170 * Parse an arithmetic expansion (indicate start of one and set state)
11171 */
Eric Andersenc470f442003-07-28 09:56:35 +000011172parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011173 if (++arinest == 1) {
11174 prevsyntax = syntax;
11175 syntax = ARISYNTAX;
11176 USTPUTC(CTLARI, out);
11177 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011178 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011179 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011180 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011181 } else {
11182 /*
11183 * we collapse embedded arithmetic expansion to
11184 * parenthesis, which should be equivalent
11185 */
11186 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011187 }
Eric Andersenc470f442003-07-28 09:56:35 +000011188 goto parsearith_return;
11189}
11190#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011191
Eric Andersenc470f442003-07-28 09:56:35 +000011192} /* end of readtoken */
11193
Eric Andersencb57d552001-06-28 07:25:16 +000011194/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011195 * Read the next input token.
11196 * If the token is a word, we set backquotelist to the list of cmds in
11197 * backquotes. We set quoteflag to true if any part of the word was
11198 * quoted.
11199 * If the token is TREDIR, then we set redirnode to a structure containing
11200 * the redirection.
11201 * In all cases, the variable startlinno is set to the number of the line
11202 * on which the token starts.
11203 *
11204 * [Change comment: here documents and internal procedures]
11205 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11206 * word parsing code into a separate routine. In this case, readtoken
11207 * doesn't need to have any internal procedures, but parseword does.
11208 * We could also make parseoperator in essence the main routine, and
11209 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011210 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011211#define NEW_xxreadtoken
11212#ifdef NEW_xxreadtoken
11213/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011214static const char xxreadtoken_chars[7] ALIGN1 = {
11215 '\n', '(', ')', '&', '|', ';', 0
11216};
Eric Andersencb57d552001-06-28 07:25:16 +000011217
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011218static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011219 TNL, TLP, TRP, /* only single occurrence allowed */
11220 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11221 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011222 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011223};
11224
11225#define xxreadtoken_doubles \
11226 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11227#define xxreadtoken_singles \
11228 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11229
11230static int
11231xxreadtoken(void)
11232{
11233 int c;
11234
11235 if (tokpushback) {
11236 tokpushback = 0;
11237 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011238 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011239 if (needprompt) {
11240 setprompt(2);
11241 }
11242 startlinno = plinno;
11243 for (;;) { /* until token or start of word found */
11244 c = pgetc_macro();
11245
11246 if ((c != ' ') && (c != '\t')
11247#if ENABLE_ASH_ALIAS
11248 && (c != PEOA)
11249#endif
11250 ) {
11251 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011252 while ((c = pgetc()) != '\n' && c != PEOF)
11253 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011254 pungetc();
11255 } else if (c == '\\') {
11256 if (pgetc() != '\n') {
11257 pungetc();
11258 goto READTOKEN1;
11259 }
11260 startlinno = ++plinno;
11261 if (doprompt)
11262 setprompt(2);
11263 } else {
11264 const char *p
11265 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11266
11267 if (c != PEOF) {
11268 if (c == '\n') {
11269 plinno++;
11270 needprompt = doprompt;
11271 }
11272
11273 p = strchr(xxreadtoken_chars, c);
11274 if (p == NULL) {
11275 READTOKEN1:
11276 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11277 }
11278
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011279 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011280 if (pgetc() == *p) { /* double occurrence? */
11281 p += xxreadtoken_doubles + 1;
11282 } else {
11283 pungetc();
11284 }
11285 }
11286 }
Denis Vlasenko2b75a942008-06-23 13:06:34 +000011287 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11288 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011289 }
11290 }
11291 } /* for */
11292}
11293#else
11294#define RETURN(token) return lasttoken = token
11295static int
11296xxreadtoken(void)
11297{
11298 int c;
11299
11300 if (tokpushback) {
11301 tokpushback = 0;
11302 return lasttoken;
11303 }
11304 if (needprompt) {
11305 setprompt(2);
11306 }
11307 startlinno = plinno;
11308 for (;;) { /* until token or start of word found */
11309 c = pgetc_macro();
11310 switch (c) {
11311 case ' ': case '\t':
11312#if ENABLE_ASH_ALIAS
11313 case PEOA:
11314#endif
11315 continue;
11316 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011317 while ((c = pgetc()) != '\n' && c != PEOF)
11318 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011319 pungetc();
11320 continue;
11321 case '\\':
11322 if (pgetc() == '\n') {
11323 startlinno = ++plinno;
11324 if (doprompt)
11325 setprompt(2);
11326 continue;
11327 }
11328 pungetc();
11329 goto breakloop;
11330 case '\n':
11331 plinno++;
11332 needprompt = doprompt;
11333 RETURN(TNL);
11334 case PEOF:
11335 RETURN(TEOF);
11336 case '&':
11337 if (pgetc() == '&')
11338 RETURN(TAND);
11339 pungetc();
11340 RETURN(TBACKGND);
11341 case '|':
11342 if (pgetc() == '|')
11343 RETURN(TOR);
11344 pungetc();
11345 RETURN(TPIPE);
11346 case ';':
11347 if (pgetc() == ';')
11348 RETURN(TENDCASE);
11349 pungetc();
11350 RETURN(TSEMI);
11351 case '(':
11352 RETURN(TLP);
11353 case ')':
11354 RETURN(TRP);
11355 default:
11356 goto breakloop;
11357 }
11358 }
11359 breakloop:
11360 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11361#undef RETURN
11362}
11363#endif /* NEW_xxreadtoken */
11364
11365static int
11366readtoken(void)
11367{
11368 int t;
11369#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011370 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011371#endif
11372
11373#if ENABLE_ASH_ALIAS
11374 top:
11375#endif
11376
11377 t = xxreadtoken();
11378
11379 /*
11380 * eat newlines
11381 */
11382 if (checkkwd & CHKNL) {
11383 while (t == TNL) {
11384 parseheredoc();
11385 t = xxreadtoken();
11386 }
11387 }
11388
11389 if (t != TWORD || quoteflag) {
11390 goto out;
11391 }
11392
11393 /*
11394 * check for keywords
11395 */
11396 if (checkkwd & CHKKWD) {
11397 const char *const *pp;
11398
11399 pp = findkwd(wordtext);
11400 if (pp) {
11401 lasttoken = t = pp - tokname_array;
11402 TRACE(("keyword %s recognized\n", tokname(t)));
11403 goto out;
11404 }
11405 }
11406
11407 if (checkkwd & CHKALIAS) {
11408#if ENABLE_ASH_ALIAS
11409 struct alias *ap;
11410 ap = lookupalias(wordtext, 1);
11411 if (ap != NULL) {
11412 if (*ap->val) {
11413 pushstring(ap->val, ap);
11414 }
11415 goto top;
11416 }
11417#endif
11418 }
11419 out:
11420 checkkwd = 0;
11421#if DEBUG
11422 if (!alreadyseen)
11423 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11424 else
11425 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11426#endif
11427 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011428}
11429
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011430static char
11431peektoken(void)
11432{
11433 int t;
11434
11435 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011436 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011437 return tokname_array[t][0];
11438}
Eric Andersencb57d552001-06-28 07:25:16 +000011439
11440/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011441 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11442 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011443 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011444static union node *
11445parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011446{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011447 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011448
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011449 tokpushback = 0;
11450 doprompt = interact;
11451 if (doprompt)
11452 setprompt(doprompt);
11453 needprompt = 0;
11454 t = readtoken();
11455 if (t == TEOF)
11456 return NEOF;
11457 if (t == TNL)
11458 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011459 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011460 return list(1);
11461}
11462
11463/*
11464 * Input any here documents.
11465 */
11466static void
11467parseheredoc(void)
11468{
11469 struct heredoc *here;
11470 union node *n;
11471
11472 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011473 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011474
11475 while (here) {
11476 if (needprompt) {
11477 setprompt(2);
11478 }
11479 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11480 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011481 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011482 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011483 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011484 n->narg.text = wordtext;
11485 n->narg.backquote = backquotelist;
11486 here->here->nhere.doc = n;
11487 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011488 }
Eric Andersencb57d552001-06-28 07:25:16 +000011489}
11490
11491
11492/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011493 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011494 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011495#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011496static const char *
11497expandstr(const char *ps)
11498{
11499 union node n;
11500
11501 /* XXX Fix (char *) cast. */
11502 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011503 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011504 popfile();
11505
11506 n.narg.type = NARG;
11507 n.narg.next = NULL;
11508 n.narg.text = wordtext;
11509 n.narg.backquote = backquotelist;
11510
11511 expandarg(&n, NULL, 0);
11512 return stackblock();
11513}
11514#endif
11515
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011516/*
11517 * Execute a command or commands contained in a string.
11518 */
11519static int
11520evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011521{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011522 union node *n;
11523 struct stackmark smark;
11524 int skip;
11525
11526 setinputstring(s);
11527 setstackmark(&smark);
11528
11529 skip = 0;
11530 while ((n = parsecmd(0)) != NEOF) {
11531 evaltree(n, 0);
11532 popstackmark(&smark);
11533 skip = evalskip;
11534 if (skip)
11535 break;
11536 }
11537 popfile();
11538
11539 skip &= mask;
11540 evalskip = skip;
11541 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011542}
11543
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011544/*
11545 * The eval command.
11546 */
11547static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011548evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011549{
11550 char *p;
11551 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011552
Denis Vlasenko68404f12008-03-17 09:00:54 +000011553 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011554 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011555 argv += 2;
11556 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011557 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011558 for (;;) {
11559 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011560 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011561 if (p == NULL)
11562 break;
11563 STPUTC(' ', concat);
11564 }
11565 STPUTC('\0', concat);
11566 p = grabstackstr(concat);
11567 }
11568 evalstring(p, ~SKIPEVAL);
11569
11570 }
11571 return exitstatus;
11572}
11573
11574/*
11575 * Read and execute commands. "Top" is nonzero for the top level command
11576 * loop; it turns on prompting if the shell is interactive.
11577 */
11578static int
11579cmdloop(int top)
11580{
11581 union node *n;
11582 struct stackmark smark;
11583 int inter;
11584 int numeof = 0;
11585
11586 TRACE(("cmdloop(%d) called\n", top));
11587 for (;;) {
11588 int skip;
11589
11590 setstackmark(&smark);
11591#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011592 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011593 showjobs(stderr, SHOW_CHANGED);
11594#endif
11595 inter = 0;
11596 if (iflag && top) {
11597 inter++;
11598#if ENABLE_ASH_MAIL
11599 chkmail();
11600#endif
11601 }
11602 n = parsecmd(inter);
11603 /* showtree(n); DEBUG */
11604 if (n == NEOF) {
11605 if (!top || numeof >= 50)
11606 break;
11607 if (!stoppedjobs()) {
11608 if (!Iflag)
11609 break;
11610 out2str("\nUse \"exit\" to leave shell.\n");
11611 }
11612 numeof++;
11613 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011614 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11615 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011616 numeof = 0;
11617 evaltree(n, 0);
11618 }
11619 popstackmark(&smark);
11620 skip = evalskip;
11621
11622 if (skip) {
11623 evalskip = 0;
11624 return skip & SKIPEVAL;
11625 }
11626 }
11627 return 0;
11628}
11629
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011630/*
11631 * Take commands from a file. To be compatible we should do a path
11632 * search for the file, which is necessary to find sub-commands.
11633 */
11634static char *
11635find_dot_file(char *name)
11636{
11637 char *fullname;
11638 const char *path = pathval();
11639 struct stat statb;
11640
11641 /* don't try this for absolute or relative paths */
11642 if (strchr(name, '/'))
11643 return name;
11644
11645 while ((fullname = padvance(&path, name)) != NULL) {
11646 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11647 /*
11648 * Don't bother freeing here, since it will
11649 * be freed by the caller.
11650 */
11651 return fullname;
11652 }
11653 stunalloc(fullname);
11654 }
11655
11656 /* not found in the PATH */
11657 ash_msg_and_raise_error("%s: not found", name);
11658 /* NOTREACHED */
11659}
11660
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011661static int
11662dotcmd(int argc, char **argv)
11663{
11664 struct strlist *sp;
11665 volatile struct shparam saveparam;
11666 int status = 0;
11667
11668 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011669 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011670
Denis Vlasenko68404f12008-03-17 09:00:54 +000011671 if (argv[1]) { /* That's what SVR2 does */
11672 char *fullname = find_dot_file(argv[1]);
11673 argv += 2;
11674 argc -= 2;
11675 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011676 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011677 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011678 shellparam.nparam = argc;
11679 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011680 };
11681
11682 setinputfile(fullname, INPUT_PUSH_FILE);
11683 commandname = fullname;
11684 cmdloop(0);
11685 popfile();
11686
Denis Vlasenko68404f12008-03-17 09:00:54 +000011687 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011688 freeparam(&shellparam);
11689 shellparam = saveparam;
11690 };
11691 status = exitstatus;
11692 }
11693 return status;
11694}
11695
11696static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011697exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011698{
11699 if (stoppedjobs())
11700 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011701 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011702 exitstatus = number(argv[1]);
11703 raise_exception(EXEXIT);
11704 /* NOTREACHED */
11705}
11706
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011707/*
11708 * Read a file containing shell functions.
11709 */
11710static void
11711readcmdfile(char *name)
11712{
11713 setinputfile(name, INPUT_PUSH_FILE);
11714 cmdloop(0);
11715 popfile();
11716}
11717
11718
Denis Vlasenkocc571512007-02-23 21:10:35 +000011719/* ============ find_command inplementation */
11720
11721/*
11722 * Resolve a command name. If you change this routine, you may have to
11723 * change the shellexec routine as well.
11724 */
11725static void
11726find_command(char *name, struct cmdentry *entry, int act, const char *path)
11727{
11728 struct tblentry *cmdp;
11729 int idx;
11730 int prev;
11731 char *fullname;
11732 struct stat statb;
11733 int e;
11734 int updatetbl;
11735 struct builtincmd *bcmd;
11736
11737 /* If name contains a slash, don't use PATH or hash table */
11738 if (strchr(name, '/') != NULL) {
11739 entry->u.index = -1;
11740 if (act & DO_ABS) {
11741 while (stat(name, &statb) < 0) {
11742#ifdef SYSV
11743 if (errno == EINTR)
11744 continue;
11745#endif
11746 entry->cmdtype = CMDUNKNOWN;
11747 return;
11748 }
11749 }
11750 entry->cmdtype = CMDNORMAL;
11751 return;
11752 }
11753
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011754/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011755
11756 updatetbl = (path == pathval());
11757 if (!updatetbl) {
11758 act |= DO_ALTPATH;
11759 if (strstr(path, "%builtin") != NULL)
11760 act |= DO_ALTBLTIN;
11761 }
11762
11763 /* If name is in the table, check answer will be ok */
11764 cmdp = cmdlookup(name, 0);
11765 if (cmdp != NULL) {
11766 int bit;
11767
11768 switch (cmdp->cmdtype) {
11769 default:
11770#if DEBUG
11771 abort();
11772#endif
11773 case CMDNORMAL:
11774 bit = DO_ALTPATH;
11775 break;
11776 case CMDFUNCTION:
11777 bit = DO_NOFUNC;
11778 break;
11779 case CMDBUILTIN:
11780 bit = DO_ALTBLTIN;
11781 break;
11782 }
11783 if (act & bit) {
11784 updatetbl = 0;
11785 cmdp = NULL;
11786 } else if (cmdp->rehash == 0)
11787 /* if not invalidated by cd, we're done */
11788 goto success;
11789 }
11790
11791 /* If %builtin not in path, check for builtin next */
11792 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011793 if (bcmd) {
11794 if (IS_BUILTIN_REGULAR(bcmd))
11795 goto builtin_success;
11796 if (act & DO_ALTPATH) {
11797 if (!(act & DO_ALTBLTIN))
11798 goto builtin_success;
11799 } else if (builtinloc <= 0) {
11800 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011801 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011802 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011803
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011804#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011805 {
11806 int applet_no = find_applet_by_name(name);
11807 if (applet_no >= 0) {
11808 entry->cmdtype = CMDNORMAL;
11809 entry->u.index = -2 - applet_no;
11810 return;
11811 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011812 }
11813#endif
11814
Denis Vlasenkocc571512007-02-23 21:10:35 +000011815 /* We have to search path. */
11816 prev = -1; /* where to start */
11817 if (cmdp && cmdp->rehash) { /* doing a rehash */
11818 if (cmdp->cmdtype == CMDBUILTIN)
11819 prev = builtinloc;
11820 else
11821 prev = cmdp->param.index;
11822 }
11823
11824 e = ENOENT;
11825 idx = -1;
11826 loop:
11827 while ((fullname = padvance(&path, name)) != NULL) {
11828 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011829 /* NB: code below will still use fullname
11830 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011831 idx++;
11832 if (pathopt) {
11833 if (prefix(pathopt, "builtin")) {
11834 if (bcmd)
11835 goto builtin_success;
11836 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011837 }
11838 if ((act & DO_NOFUNC)
11839 || !prefix(pathopt, "func")
11840 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011841 continue;
11842 }
11843 }
11844 /* if rehash, don't redo absolute path names */
11845 if (fullname[0] == '/' && idx <= prev) {
11846 if (idx < prev)
11847 continue;
11848 TRACE(("searchexec \"%s\": no change\n", name));
11849 goto success;
11850 }
11851 while (stat(fullname, &statb) < 0) {
11852#ifdef SYSV
11853 if (errno == EINTR)
11854 continue;
11855#endif
11856 if (errno != ENOENT && errno != ENOTDIR)
11857 e = errno;
11858 goto loop;
11859 }
11860 e = EACCES; /* if we fail, this will be the error */
11861 if (!S_ISREG(statb.st_mode))
11862 continue;
11863 if (pathopt) { /* this is a %func directory */
11864 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011865 /* NB: stalloc will return space pointed by fullname
11866 * (because we don't have any intervening allocations
11867 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011868 readcmdfile(fullname);
11869 cmdp = cmdlookup(name, 0);
11870 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11871 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11872 stunalloc(fullname);
11873 goto success;
11874 }
11875 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11876 if (!updatetbl) {
11877 entry->cmdtype = CMDNORMAL;
11878 entry->u.index = idx;
11879 return;
11880 }
11881 INT_OFF;
11882 cmdp = cmdlookup(name, 1);
11883 cmdp->cmdtype = CMDNORMAL;
11884 cmdp->param.index = idx;
11885 INT_ON;
11886 goto success;
11887 }
11888
11889 /* We failed. If there was an entry for this command, delete it */
11890 if (cmdp && updatetbl)
11891 delete_cmd_entry();
11892 if (act & DO_ERR)
11893 ash_msg("%s: %s", name, errmsg(e, "not found"));
11894 entry->cmdtype = CMDUNKNOWN;
11895 return;
11896
11897 builtin_success:
11898 if (!updatetbl) {
11899 entry->cmdtype = CMDBUILTIN;
11900 entry->u.cmd = bcmd;
11901 return;
11902 }
11903 INT_OFF;
11904 cmdp = cmdlookup(name, 1);
11905 cmdp->cmdtype = CMDBUILTIN;
11906 cmdp->param.cmd = bcmd;
11907 INT_ON;
11908 success:
11909 cmdp->rehash = 0;
11910 entry->cmdtype = cmdp->cmdtype;
11911 entry->u = cmdp->param;
11912}
11913
11914
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011915/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011916
Eric Andersencb57d552001-06-28 07:25:16 +000011917/*
Eric Andersencb57d552001-06-28 07:25:16 +000011918 * The trap builtin.
11919 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011920static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011921trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011922{
11923 char *action;
11924 char **ap;
11925 int signo;
11926
Eric Andersenc470f442003-07-28 09:56:35 +000011927 nextopt(nullstr);
11928 ap = argptr;
11929 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011930 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011931 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011932 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000011933 single_quote(trap[signo]),
11934 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000011935 }
11936 }
11937 return 0;
11938 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000011939 action = NULL;
11940 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011941 action = *ap++;
11942 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011943 signo = get_signum(*ap);
11944 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011945 ash_msg_and_raise_error("%s: bad trap", *ap);
11946 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011947 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011948 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011949 action = NULL;
11950 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011951 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011952 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011953 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011954 trap[signo] = action;
11955 if (signo != 0)
11956 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011957 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011958 ap++;
11959 }
11960 return 0;
11961}
11962
Eric Andersenc470f442003-07-28 09:56:35 +000011963
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011964/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011965
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011966#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011967/*
11968 * Lists available builtins
11969 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011970static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011971helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011972{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011973 unsigned col;
11974 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000011975
11976 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011977 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011978 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011979 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011980 if (col > 60) {
11981 out1fmt("\n");
11982 col = 0;
11983 }
11984 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011985#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011986 {
11987 const char *a = applet_names;
11988 while (*a) {
11989 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11990 if (col > 60) {
11991 out1fmt("\n");
11992 col = 0;
11993 }
11994 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011995 }
11996 }
11997#endif
11998 out1fmt("\n\n");
11999 return EXIT_SUCCESS;
12000}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012001#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012002
Eric Andersencb57d552001-06-28 07:25:16 +000012003/*
Eric Andersencb57d552001-06-28 07:25:16 +000012004 * The export and readonly commands.
12005 */
Eric Andersenc470f442003-07-28 09:56:35 +000012006static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012007exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012008{
12009 struct var *vp;
12010 char *name;
12011 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012012 char **aptr;
12013 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012014
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012015 if (nextopt("p") != 'p') {
12016 aptr = argptr;
12017 name = *aptr;
12018 if (name) {
12019 do {
12020 p = strchr(name, '=');
12021 if (p != NULL) {
12022 p++;
12023 } else {
12024 vp = *findvar(hashvar(name), name);
12025 if (vp) {
12026 vp->flags |= flag;
12027 continue;
12028 }
Eric Andersencb57d552001-06-28 07:25:16 +000012029 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012030 setvar(name, p, flag);
12031 } while ((name = *++aptr) != NULL);
12032 return 0;
12033 }
Eric Andersencb57d552001-06-28 07:25:16 +000012034 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012035 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012036 return 0;
12037}
12038
Eric Andersencb57d552001-06-28 07:25:16 +000012039/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012040 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012041 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012042static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012043unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012044{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012045 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012046
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012047 cmdp = cmdlookup(name, 0);
12048 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12049 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012050}
12051
Eric Andersencb57d552001-06-28 07:25:16 +000012052/*
Eric Andersencb57d552001-06-28 07:25:16 +000012053 * The unset builtin command. We unset the function before we unset the
12054 * variable to allow a function to be unset when there is a readonly variable
12055 * with the same name.
12056 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012057static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012058unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012059{
12060 char **ap;
12061 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012062 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012063 int ret = 0;
12064
12065 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012066 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012067 }
Eric Andersencb57d552001-06-28 07:25:16 +000012068
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012069 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012070 if (flag != 'f') {
12071 i = unsetvar(*ap);
12072 ret |= i;
12073 if (!(i & 2))
12074 continue;
12075 }
12076 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012077 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012078 }
Eric Andersenc470f442003-07-28 09:56:35 +000012079 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012080}
12081
12082
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012083/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012084
Eric Andersenc470f442003-07-28 09:56:35 +000012085#include <sys/times.h>
12086
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012087static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012088 ' ', offsetof(struct tms, tms_utime),
12089 '\n', offsetof(struct tms, tms_stime),
12090 ' ', offsetof(struct tms, tms_cutime),
12091 '\n', offsetof(struct tms, tms_cstime),
12092 0
12093};
Eric Andersencb57d552001-06-28 07:25:16 +000012094
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012095static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012096timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012097{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012098 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012099 const unsigned char *p;
12100 struct tms buf;
12101
12102 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012103 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012104
12105 p = timescmd_str;
12106 do {
12107 t = *(clock_t *)(((char *) &buf) + p[1]);
12108 s = t / clk_tck;
12109 out1fmt("%ldm%ld.%.3lds%c",
12110 s/60, s%60,
12111 ((t - s * clk_tck) * 1000) / clk_tck,
12112 p[0]);
12113 } while (*(p += 2));
12114
Eric Andersencb57d552001-06-28 07:25:16 +000012115 return 0;
12116}
12117
Denis Vlasenko131ae172007-02-18 13:00:19 +000012118#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012119static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012120dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012121{
Eric Andersened9ecf72004-06-22 08:29:45 +000012122 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012123 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012124
Denis Vlasenkob012b102007-02-19 22:43:01 +000012125 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012126 result = arith(s, &errcode);
12127 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012128 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012129 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012130 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012131 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012132 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012133 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012134 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012135 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012136 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012137
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012138 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012139}
Eric Andersenc470f442003-07-28 09:56:35 +000012140
Eric Andersenc470f442003-07-28 09:56:35 +000012141/*
Eric Andersen90898442003-08-06 11:20:52 +000012142 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12143 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12144 *
12145 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012146 */
12147static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012148letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012149{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012150 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012151
Denis Vlasenko68404f12008-03-17 09:00:54 +000012152 argv++;
12153 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012154 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012155 do {
12156 i = dash_arith(*argv);
12157 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012158
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012159 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012160}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012161#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012162
Eric Andersenc470f442003-07-28 09:56:35 +000012163
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012164/* ============ miscbltin.c
12165 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012166 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012167 */
12168
12169#undef rflag
12170
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012171#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012172typedef enum __rlimit_resource rlim_t;
12173#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012174
Eric Andersenc470f442003-07-28 09:56:35 +000012175/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012176 * The read builtin. Options:
12177 * -r Do not interpret '\' specially
12178 * -s Turn off echo (tty only)
12179 * -n NCHARS Read NCHARS max
12180 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12181 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12182 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012183 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012184 * TODO: bash also has:
12185 * -a ARRAY Read into array[0],[1],etc
12186 * -d DELIM End on DELIM char, not newline
12187 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012188 */
Eric Andersenc470f442003-07-28 09:56:35 +000012189static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012190readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012191{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012192 static const char *const arg_REPLY[] = { "REPLY", NULL };
12193
Eric Andersenc470f442003-07-28 09:56:35 +000012194 char **ap;
12195 int backslash;
12196 char c;
12197 int rflag;
12198 char *prompt;
12199 const char *ifs;
12200 char *p;
12201 int startword;
12202 int status;
12203 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012204 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012205#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012206 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012207 int silent = 0;
12208 struct termios tty, old_tty;
12209#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012210#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012211 unsigned end_ms = 0;
12212 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012213#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012214
12215 rflag = 0;
12216 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012217 while ((i = nextopt("p:u:r"
12218 USE_ASH_READ_TIMEOUT("t:")
12219 USE_ASH_READ_NCHARS("n:s")
12220 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012221 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012222 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012223 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012224 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012225#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012226 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012227 nchars = bb_strtou(optionarg, NULL, 10);
12228 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012229 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012230 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012231 break;
12232 case 's':
12233 silent = 1;
12234 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012235#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012236#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012237 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012238 timeout = bb_strtou(optionarg, NULL, 10);
12239 if (errno || timeout > UINT_MAX / 2048)
12240 ash_msg_and_raise_error("invalid timeout");
12241 timeout *= 1000;
12242#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012243 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012244 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012245 /* EINVAL means number is ok, but not terminated by NUL */
12246 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012247 char *p2;
12248 if (*++p) {
12249 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012250 ts.tv_usec = bb_strtou(p, &p2, 10);
12251 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012252 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012253 scale = p2 - p;
12254 /* normalize to usec */
12255 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012256 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012257 while (scale++ < 6)
12258 ts.tv_usec *= 10;
12259 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012260 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012261 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012262 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012263 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012264 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012265 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012266#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012267 break;
12268#endif
12269 case 'r':
12270 rflag = 1;
12271 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012272 case 'u':
12273 fd = bb_strtou(optionarg, NULL, 10);
12274 if (fd < 0 || errno)
12275 ash_msg_and_raise_error("invalid file descriptor");
12276 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012277 default:
12278 break;
12279 }
Eric Andersenc470f442003-07-28 09:56:35 +000012280 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012281 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012282 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012283 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012284 ap = argptr;
12285 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012286 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012287 ifs = bltinlookup("IFS");
12288 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012289 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012290#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012291 tcgetattr(fd, &tty);
12292 old_tty = tty;
12293 if (nchars || silent) {
12294 if (nchars) {
12295 tty.c_lflag &= ~ICANON;
12296 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012297 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012298 if (silent) {
12299 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12300 }
12301 /* if tcgetattr failed, tcsetattr will fail too.
12302 * Ignoring, it's harmless. */
12303 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012304 }
12305#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012306
Eric Andersenc470f442003-07-28 09:56:35 +000012307 status = 0;
12308 startword = 1;
12309 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012310#if ENABLE_ASH_READ_TIMEOUT
12311 if (timeout) /* NB: ensuring end_ms is nonzero */
12312 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12313#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012314 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012315 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012316#if ENABLE_ASH_READ_TIMEOUT
12317 if (end_ms) {
12318 struct pollfd pfd[1];
12319 pfd[0].fd = fd;
12320 pfd[0].events = POLLIN;
12321 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12322 if ((int)timeout <= 0 /* already late? */
12323 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12324 ) { /* timed out! */
12325#if ENABLE_ASH_READ_NCHARS
12326 tcsetattr(fd, TCSANOW, &old_tty);
12327#endif
12328 return 1;
12329 }
12330 }
12331#endif
12332 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012333 status = 1;
12334 break;
12335 }
12336 if (c == '\0')
12337 continue;
12338 if (backslash) {
12339 backslash = 0;
12340 if (c != '\n')
12341 goto put;
12342 continue;
12343 }
12344 if (!rflag && c == '\\') {
12345 backslash++;
12346 continue;
12347 }
12348 if (c == '\n')
12349 break;
12350 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12351 continue;
12352 }
12353 startword = 0;
12354 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12355 STACKSTRNUL(p);
12356 setvar(*ap, stackblock(), 0);
12357 ap++;
12358 startword = 1;
12359 STARTSTACKSTR(p);
12360 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012361 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012362 STPUTC(c, p);
12363 }
12364 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012365/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012366#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012367 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012368#else
12369 while (1);
12370#endif
12371
12372#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012373 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012374#endif
12375
Eric Andersenc470f442003-07-28 09:56:35 +000012376 STACKSTRNUL(p);
12377 /* Remove trailing blanks */
12378 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12379 *p = '\0';
12380 setvar(*ap, stackblock(), 0);
12381 while (*++ap != NULL)
12382 setvar(*ap, nullstr, 0);
12383 return status;
12384}
12385
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012386static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012387umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012388{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012389 static const char permuser[3] ALIGN1 = "ugo";
12390 static const char permmode[3] ALIGN1 = "rwx";
12391 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012392 S_IRUSR, S_IWUSR, S_IXUSR,
12393 S_IRGRP, S_IWGRP, S_IXGRP,
12394 S_IROTH, S_IWOTH, S_IXOTH
12395 };
12396
12397 char *ap;
12398 mode_t mask;
12399 int i;
12400 int symbolic_mode = 0;
12401
12402 while (nextopt("S") != '\0') {
12403 symbolic_mode = 1;
12404 }
12405
Denis Vlasenkob012b102007-02-19 22:43:01 +000012406 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012407 mask = umask(0);
12408 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012409 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012410
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012411 ap = *argptr;
12412 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012413 if (symbolic_mode) {
12414 char buf[18];
12415 char *p = buf;
12416
12417 for (i = 0; i < 3; i++) {
12418 int j;
12419
12420 *p++ = permuser[i];
12421 *p++ = '=';
12422 for (j = 0; j < 3; j++) {
12423 if ((mask & permmask[3 * i + j]) == 0) {
12424 *p++ = permmode[j];
12425 }
12426 }
12427 *p++ = ',';
12428 }
12429 *--p = 0;
12430 puts(buf);
12431 } else {
12432 out1fmt("%.4o\n", mask);
12433 }
12434 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012435 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012436 mask = 0;
12437 do {
12438 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012439 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012440 mask = (mask << 3) + (*ap - '0');
12441 } while (*++ap != '\0');
12442 umask(mask);
12443 } else {
12444 mask = ~mask & 0777;
12445 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012446 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012447 }
12448 umask(~mask & 0777);
12449 }
12450 }
12451 return 0;
12452}
12453
12454/*
12455 * ulimit builtin
12456 *
12457 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12458 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12459 * ash by J.T. Conklin.
12460 *
12461 * Public domain.
12462 */
12463
12464struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012465 uint8_t cmd; /* RLIMIT_xxx fit into it */
12466 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012467 char option;
12468};
12469
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012470static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012471#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012472 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012473#endif
12474#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012475 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012476#endif
12477#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012478 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012479#endif
12480#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012481 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012482#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012483#ifdef RLIMIT_CORE
12484 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012485#endif
12486#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012487 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012488#endif
12489#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012490 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012491#endif
12492#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012493 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012494#endif
12495#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012496 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012497#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012498#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012499 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012500#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012501#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012502 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012503#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012504};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012505static const char limits_name[] =
12506#ifdef RLIMIT_CPU
12507 "time(seconds)" "\0"
12508#endif
12509#ifdef RLIMIT_FSIZE
12510 "file(blocks)" "\0"
12511#endif
12512#ifdef RLIMIT_DATA
12513 "data(kb)" "\0"
12514#endif
12515#ifdef RLIMIT_STACK
12516 "stack(kb)" "\0"
12517#endif
12518#ifdef RLIMIT_CORE
12519 "coredump(blocks)" "\0"
12520#endif
12521#ifdef RLIMIT_RSS
12522 "memory(kb)" "\0"
12523#endif
12524#ifdef RLIMIT_MEMLOCK
12525 "locked memory(kb)" "\0"
12526#endif
12527#ifdef RLIMIT_NPROC
12528 "process" "\0"
12529#endif
12530#ifdef RLIMIT_NOFILE
12531 "nofiles" "\0"
12532#endif
12533#ifdef RLIMIT_AS
12534 "vmemory(kb)" "\0"
12535#endif
12536#ifdef RLIMIT_LOCKS
12537 "locks" "\0"
12538#endif
12539;
Eric Andersenc470f442003-07-28 09:56:35 +000012540
Glenn L McGrath76620622004-01-13 10:19:37 +000012541enum limtype { SOFT = 0x1, HARD = 0x2 };
12542
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012543static void
12544printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012545 const struct limits *l)
12546{
12547 rlim_t val;
12548
12549 val = limit->rlim_max;
12550 if (how & SOFT)
12551 val = limit->rlim_cur;
12552
12553 if (val == RLIM_INFINITY)
12554 out1fmt("unlimited\n");
12555 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012556 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012557 out1fmt("%lld\n", (long long) val);
12558 }
12559}
12560
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012561static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012562ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012563{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012564 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012565 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012566 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012567 const struct limits *l;
12568 int set, all = 0;
12569 int optc, what;
12570 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012571
12572 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012573 while ((optc = nextopt("HSa"
12574#ifdef RLIMIT_CPU
12575 "t"
12576#endif
12577#ifdef RLIMIT_FSIZE
12578 "f"
12579#endif
12580#ifdef RLIMIT_DATA
12581 "d"
12582#endif
12583#ifdef RLIMIT_STACK
12584 "s"
12585#endif
12586#ifdef RLIMIT_CORE
12587 "c"
12588#endif
12589#ifdef RLIMIT_RSS
12590 "m"
12591#endif
12592#ifdef RLIMIT_MEMLOCK
12593 "l"
12594#endif
12595#ifdef RLIMIT_NPROC
12596 "p"
12597#endif
12598#ifdef RLIMIT_NOFILE
12599 "n"
12600#endif
12601#ifdef RLIMIT_AS
12602 "v"
12603#endif
12604#ifdef RLIMIT_LOCKS
12605 "w"
12606#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012607 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012608 switch (optc) {
12609 case 'H':
12610 how = HARD;
12611 break;
12612 case 'S':
12613 how = SOFT;
12614 break;
12615 case 'a':
12616 all = 1;
12617 break;
12618 default:
12619 what = optc;
12620 }
12621
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012622 for (l = limits_tbl; l->option != what; l++)
12623 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012624
12625 set = *argptr ? 1 : 0;
12626 if (set) {
12627 char *p = *argptr;
12628
12629 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012630 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012631 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012632 val = RLIM_INFINITY;
12633 else {
12634 val = (rlim_t) 0;
12635
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012636 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012637 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012638 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012639 if (val < (rlim_t) 0)
12640 break;
12641 }
12642 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012643 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012644 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012645 }
12646 }
12647 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012648 const char *lname = limits_name;
12649 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012650 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012651 out1fmt("%-20s ", lname);
12652 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012653 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012654 }
12655 return 0;
12656 }
12657
12658 getrlimit(l->cmd, &limit);
12659 if (set) {
12660 if (how & HARD)
12661 limit.rlim_max = val;
12662 if (how & SOFT)
12663 limit.rlim_cur = val;
12664 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012665 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012666 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012667 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012668 }
12669 return 0;
12670}
12671
Eric Andersen90898442003-08-06 11:20:52 +000012672
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012673/* ============ Math support */
12674
Denis Vlasenko131ae172007-02-18 13:00:19 +000012675#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012676
12677/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12678
12679 Permission is hereby granted, free of charge, to any person obtaining
12680 a copy of this software and associated documentation files (the
12681 "Software"), to deal in the Software without restriction, including
12682 without limitation the rights to use, copy, modify, merge, publish,
12683 distribute, sublicense, and/or sell copies of the Software, and to
12684 permit persons to whom the Software is furnished to do so, subject to
12685 the following conditions:
12686
12687 The above copyright notice and this permission notice shall be
12688 included in all copies or substantial portions of the Software.
12689
12690 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12691 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12692 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12693 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12694 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12695 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12696 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12697*/
12698
12699/* This is my infix parser/evaluator. It is optimized for size, intended
12700 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012701 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012702 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012703 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012704 * be that which POSIX specifies for shells. */
12705
12706/* The code uses a simple two-stack algorithm. See
12707 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012708 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012709 * this is based (this code differs in that it applies operators immediately
12710 * to the stack instead of adding them to a queue to end up with an
12711 * expression). */
12712
12713/* To use the routine, call it with an expression string and error return
12714 * pointer */
12715
12716/*
12717 * Aug 24, 2001 Manuel Novoa III
12718 *
12719 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12720 *
12721 * 1) In arith_apply():
12722 * a) Cached values of *numptr and &(numptr[-1]).
12723 * b) Removed redundant test for zero denominator.
12724 *
12725 * 2) In arith():
12726 * a) Eliminated redundant code for processing operator tokens by moving
12727 * to a table-based implementation. Also folded handling of parens
12728 * into the table.
12729 * b) Combined all 3 loops which called arith_apply to reduce generated
12730 * code size at the cost of speed.
12731 *
12732 * 3) The following expressions were treated as valid by the original code:
12733 * 1() , 0! , 1 ( *3 ) .
12734 * These bugs have been fixed by internally enclosing the expression in
12735 * parens and then checking that all binary ops and right parens are
12736 * preceded by a valid expression (NUM_TOKEN).
12737 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012738 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012739 * ctype's isspace() if it is used by another busybox applet or if additional
12740 * whitespace chars should be considered. Look below the "#include"s for a
12741 * precompiler test.
12742 */
12743
12744/*
12745 * Aug 26, 2001 Manuel Novoa III
12746 *
12747 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12748 *
12749 * Merge in Aaron's comments previously posted to the busybox list,
12750 * modified slightly to take account of my changes to the code.
12751 *
12752 */
12753
12754/*
12755 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12756 *
12757 * - allow access to variable,
12758 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12759 * - realize assign syntax (VAR=expr, +=, *= etc)
12760 * - realize exponentiation (** operator)
12761 * - realize comma separated - expr, expr
12762 * - realise ++expr --expr expr++ expr--
12763 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012764 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012765 * - was restored loses XOR operator
12766 * - remove one goto label, added three ;-)
12767 * - protect $((num num)) as true zero expr (Manuel`s error)
12768 * - always use special isspace(), see comment from bash ;-)
12769 */
12770
Eric Andersen90898442003-08-06 11:20:52 +000012771#define arith_isspace(arithval) \
12772 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12773
Eric Andersen90898442003-08-06 11:20:52 +000012774typedef unsigned char operator;
12775
12776/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012777 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012778 * precedence. The ID portion is so that multiple operators can have the
12779 * same precedence, ensuring that the leftmost one is evaluated first.
12780 * Consider * and /. */
12781
12782#define tok_decl(prec,id) (((id)<<5)|(prec))
12783#define PREC(op) ((op) & 0x1F)
12784
12785#define TOK_LPAREN tok_decl(0,0)
12786
12787#define TOK_COMMA tok_decl(1,0)
12788
12789#define TOK_ASSIGN tok_decl(2,0)
12790#define TOK_AND_ASSIGN tok_decl(2,1)
12791#define TOK_OR_ASSIGN tok_decl(2,2)
12792#define TOK_XOR_ASSIGN tok_decl(2,3)
12793#define TOK_PLUS_ASSIGN tok_decl(2,4)
12794#define TOK_MINUS_ASSIGN tok_decl(2,5)
12795#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12796#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12797
12798#define TOK_MUL_ASSIGN tok_decl(3,0)
12799#define TOK_DIV_ASSIGN tok_decl(3,1)
12800#define TOK_REM_ASSIGN tok_decl(3,2)
12801
12802/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012803#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012804
12805/* conditional is right associativity too */
12806#define TOK_CONDITIONAL tok_decl(4,0)
12807#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12808
12809#define TOK_OR tok_decl(5,0)
12810
12811#define TOK_AND tok_decl(6,0)
12812
12813#define TOK_BOR tok_decl(7,0)
12814
12815#define TOK_BXOR tok_decl(8,0)
12816
12817#define TOK_BAND tok_decl(9,0)
12818
12819#define TOK_EQ tok_decl(10,0)
12820#define TOK_NE tok_decl(10,1)
12821
12822#define TOK_LT tok_decl(11,0)
12823#define TOK_GT tok_decl(11,1)
12824#define TOK_GE tok_decl(11,2)
12825#define TOK_LE tok_decl(11,3)
12826
12827#define TOK_LSHIFT tok_decl(12,0)
12828#define TOK_RSHIFT tok_decl(12,1)
12829
12830#define TOK_ADD tok_decl(13,0)
12831#define TOK_SUB tok_decl(13,1)
12832
12833#define TOK_MUL tok_decl(14,0)
12834#define TOK_DIV tok_decl(14,1)
12835#define TOK_REM tok_decl(14,2)
12836
12837/* exponent is right associativity */
12838#define TOK_EXPONENT tok_decl(15,1)
12839
12840/* For now unary operators. */
12841#define UNARYPREC 16
12842#define TOK_BNOT tok_decl(UNARYPREC,0)
12843#define TOK_NOT tok_decl(UNARYPREC,1)
12844
12845#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12846#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12847
12848#define PREC_PRE (UNARYPREC+2)
12849
12850#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12851#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12852
12853#define PREC_POST (UNARYPREC+3)
12854
12855#define TOK_POST_INC tok_decl(PREC_POST, 0)
12856#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12857
12858#define SPEC_PREC (UNARYPREC+4)
12859
12860#define TOK_NUM tok_decl(SPEC_PREC, 0)
12861#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12862
12863#define NUMPTR (*numstackptr)
12864
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012865static int
12866tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012867{
12868 operator prec = PREC(op);
12869
12870 convert_prec_is_assing(prec);
12871 return (prec == PREC(TOK_ASSIGN) ||
12872 prec == PREC_PRE || prec == PREC_POST);
12873}
12874
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012875static int
12876is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012877{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012878 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12879 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012880}
12881
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012882typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012883 arith_t val;
12884 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012885 char contidional_second_val_initialized;
12886 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012887 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012888} v_n_t;
12889
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012890typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012891 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012892 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012893} chk_var_recursive_looped_t;
12894
12895static chk_var_recursive_looped_t *prev_chk_var_recursive;
12896
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012897static int
12898arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012899{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012900 if (t->var) {
12901 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012902
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012903 if (p) {
12904 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012905
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012906 /* recursive try as expression */
12907 chk_var_recursive_looped_t *cur;
12908 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012909
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012910 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12911 if (strcmp(cur->var, t->var) == 0) {
12912 /* expression recursion loop detected */
12913 return -5;
12914 }
12915 }
12916 /* save current lookuped var name */
12917 cur = prev_chk_var_recursive;
12918 cur_save.var = t->var;
12919 cur_save.next = cur;
12920 prev_chk_var_recursive = &cur_save;
12921
12922 t->val = arith (p, &errcode);
12923 /* restore previous ptr after recursiving */
12924 prev_chk_var_recursive = cur;
12925 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012926 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012927 /* allow undefined var as 0 */
12928 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012929 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012930 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012931}
12932
12933/* "applying" a token means performing it on the top elements on the integer
12934 * stack. For a unary operator it will only change the top element, but a
12935 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012936static int
12937arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012938{
Eric Andersen90898442003-08-06 11:20:52 +000012939 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012940 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012941 int ret_arith_lookup_val;
12942
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012943 /* There is no operator that can work without arguments */
12944 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012945 numptr_m1 = NUMPTR - 1;
12946
12947 /* check operand is var with noninteger value */
12948 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012949 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012950 return ret_arith_lookup_val;
12951
12952 rez = numptr_m1->val;
12953 if (op == TOK_UMINUS)
12954 rez *= -1;
12955 else if (op == TOK_NOT)
12956 rez = !rez;
12957 else if (op == TOK_BNOT)
12958 rez = ~rez;
12959 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12960 rez++;
12961 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12962 rez--;
12963 else if (op != TOK_UPLUS) {
12964 /* Binary operators */
12965
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012966 /* check and binary operators need two arguments */
12967 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012968
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012969 /* ... and they pop one */
12970 --NUMPTR;
12971 numptr_val = rez;
12972 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000012973 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012974 /* protect $((expr1 ? expr2)) without ": expr" */
12975 goto err;
12976 }
12977 rez = numptr_m1->contidional_second_val;
12978 } else if (numptr_m1->contidional_second_val_initialized) {
12979 /* protect $((expr1 : expr2)) without "expr ? " */
12980 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012981 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012982 numptr_m1 = NUMPTR - 1;
12983 if (op != TOK_ASSIGN) {
12984 /* check operand is var with noninteger value for not '=' */
12985 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12986 if (ret_arith_lookup_val)
12987 return ret_arith_lookup_val;
12988 }
12989 if (op == TOK_CONDITIONAL) {
12990 numptr_m1->contidional_second_val = rez;
12991 }
12992 rez = numptr_m1->val;
12993 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012994 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012995 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012996 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012997 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012998 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012999 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013000 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013001 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013002 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013003 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013004 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013005 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013006 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013007 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013008 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013009 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013010 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013011 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013012 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013013 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013014 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013015 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013016 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013018 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013019 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013020 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013021 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013022 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013023 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013024 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013025 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013026 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013027 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013028 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013029 /* protect $((expr : expr)) without "expr ? " */
13030 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013031 }
13032 numptr_m1->contidional_second_val_initialized = op;
13033 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013034 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013035 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013036 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013037 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013038 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013039 return -3; /* exponent less than 0 */
13040 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013041 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013042
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013043 if (numptr_val)
13044 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013045 c *= rez;
13046 rez = c;
13047 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013048 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013049 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013050 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013051 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013052 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013053 rez %= numptr_val;
13054 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013055 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013056 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013057
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013058 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013059 /* Hmm, 1=2 ? */
13060 goto err;
13061 }
13062 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013063#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013064 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013065#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013066 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013067#endif
Eric Andersen90898442003-08-06 11:20:52 +000013068 setvar(numptr_m1->var, buf, 0);
13069 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013070 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013071 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013072 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013073 rez++;
13074 }
13075 numptr_m1->val = rez;
13076 /* protect geting var value, is number now */
13077 numptr_m1->var = NULL;
13078 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013079 err:
13080 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013081}
13082
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013083/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013084static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013085 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13086 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13087 '<','<', 0, TOK_LSHIFT,
13088 '>','>', 0, TOK_RSHIFT,
13089 '|','|', 0, TOK_OR,
13090 '&','&', 0, TOK_AND,
13091 '!','=', 0, TOK_NE,
13092 '<','=', 0, TOK_LE,
13093 '>','=', 0, TOK_GE,
13094 '=','=', 0, TOK_EQ,
13095 '|','=', 0, TOK_OR_ASSIGN,
13096 '&','=', 0, TOK_AND_ASSIGN,
13097 '*','=', 0, TOK_MUL_ASSIGN,
13098 '/','=', 0, TOK_DIV_ASSIGN,
13099 '%','=', 0, TOK_REM_ASSIGN,
13100 '+','=', 0, TOK_PLUS_ASSIGN,
13101 '-','=', 0, TOK_MINUS_ASSIGN,
13102 '-','-', 0, TOK_POST_DEC,
13103 '^','=', 0, TOK_XOR_ASSIGN,
13104 '+','+', 0, TOK_POST_INC,
13105 '*','*', 0, TOK_EXPONENT,
13106 '!', 0, TOK_NOT,
13107 '<', 0, TOK_LT,
13108 '>', 0, TOK_GT,
13109 '=', 0, TOK_ASSIGN,
13110 '|', 0, TOK_BOR,
13111 '&', 0, TOK_BAND,
13112 '*', 0, TOK_MUL,
13113 '/', 0, TOK_DIV,
13114 '%', 0, TOK_REM,
13115 '+', 0, TOK_ADD,
13116 '-', 0, TOK_SUB,
13117 '^', 0, TOK_BXOR,
13118 /* uniq */
13119 '~', 0, TOK_BNOT,
13120 ',', 0, TOK_COMMA,
13121 '?', 0, TOK_CONDITIONAL,
13122 ':', 0, TOK_CONDITIONAL_SEP,
13123 ')', 0, TOK_RPAREN,
13124 '(', 0, TOK_LPAREN,
13125 0
13126};
13127/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013128#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013129
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013130static arith_t
13131arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013132{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013133 char arithval; /* Current character under analysis */
13134 operator lasttok, op;
13135 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013136 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013137 const char *p = endexpression;
13138 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013139 v_n_t *numstack, *numstackptr;
13140 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013141
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013142 /* Stack of integers */
13143 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13144 * in any given correct or incorrect expression is left as an exercise to
13145 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013146 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013147 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013148 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013149
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013150 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13151 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013152
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013153 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013154 arithval = *expr;
13155 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013156 if (p == endexpression) {
13157 /* Null expression. */
13158 return 0;
13159 }
13160
13161 /* This is only reached after all tokens have been extracted from the
13162 * input stream. If there are still tokens on the operator stack, they
13163 * are to be applied in order. At the end, there should be a final
13164 * result on the integer stack */
13165
13166 if (expr != endexpression + 1) {
13167 /* If we haven't done so already, */
13168 /* append a closing right paren */
13169 expr = endexpression;
13170 /* and let the loop process it. */
13171 continue;
13172 }
13173 /* At this point, we're done with the expression. */
13174 if (numstackptr != numstack+1) {
13175 /* ... but if there isn't, it's bad */
13176 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013177 *perrcode = -1;
13178 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013179 }
13180 if (numstack->var) {
13181 /* expression is $((var)) only, lookup now */
13182 errcode = arith_lookup_val(numstack);
13183 }
13184 ret:
13185 *perrcode = errcode;
13186 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013187 }
13188
Eric Andersen90898442003-08-06 11:20:52 +000013189 /* Continue processing the expression. */
13190 if (arith_isspace(arithval)) {
13191 /* Skip whitespace */
13192 goto prologue;
13193 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013194 p = endofname(expr);
13195 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013196 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013197
13198 numstackptr->var = alloca(var_name_size);
13199 safe_strncpy(numstackptr->var, expr, var_name_size);
13200 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013201 num:
Eric Andersen90898442003-08-06 11:20:52 +000013202 numstackptr->contidional_second_val_initialized = 0;
13203 numstackptr++;
13204 lasttok = TOK_NUM;
13205 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013206 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013207 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013208 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013209#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013210 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013211#else
13212 numstackptr->val = strtol(expr, (char **) &expr, 0);
13213#endif
Eric Andersen90898442003-08-06 11:20:52 +000013214 goto num;
13215 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013216 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013217 const char *o;
13218
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013219 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013220 /* strange operator not found */
13221 goto err;
13222 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013223 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013224 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013225 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013226 /* found */
13227 expr = o - 1;
13228 break;
13229 }
13230 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013231 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013232 p++;
13233 /* skip zero delim */
13234 p++;
13235 }
13236 op = p[1];
13237
13238 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013239 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13240 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013241
13242 /* Plus and minus are binary (not unary) _only_ if the last
13243 * token was as number, or a right paren (which pretends to be
13244 * a number, since it evaluates to one). Think about it.
13245 * It makes sense. */
13246 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013247 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013248 case TOK_ADD:
13249 op = TOK_UPLUS;
13250 break;
13251 case TOK_SUB:
13252 op = TOK_UMINUS;
13253 break;
13254 case TOK_POST_INC:
13255 op = TOK_PRE_INC;
13256 break;
13257 case TOK_POST_DEC:
13258 op = TOK_PRE_DEC;
13259 break;
Eric Andersen90898442003-08-06 11:20:52 +000013260 }
13261 }
13262 /* We don't want a unary operator to cause recursive descent on the
13263 * stack, because there can be many in a row and it could cause an
13264 * operator to be evaluated before its argument is pushed onto the
13265 * integer stack. */
13266 /* But for binary operators, "apply" everything on the operator
13267 * stack until we find an operator with a lesser priority than the
13268 * one we have just extracted. */
13269 /* Left paren is given the lowest priority so it will never be
13270 * "applied" in this way.
13271 * if associativity is right and priority eq, applied also skip
13272 */
13273 prec = PREC(op);
13274 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13275 /* not left paren or unary */
13276 if (lasttok != TOK_NUM) {
13277 /* binary op must be preceded by a num */
13278 goto err;
13279 }
13280 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013281 if (op == TOK_RPAREN) {
13282 /* The algorithm employed here is simple: while we don't
13283 * hit an open paren nor the bottom of the stack, pop
13284 * tokens and apply them */
13285 if (stackptr[-1] == TOK_LPAREN) {
13286 --stackptr;
13287 /* Any operator directly after a */
13288 lasttok = TOK_NUM;
13289 /* close paren should consider itself binary */
13290 goto prologue;
13291 }
13292 } else {
13293 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013294
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013295 convert_prec_is_assing(prec);
13296 convert_prec_is_assing(prev_prec);
13297 if (prev_prec < prec)
13298 break;
13299 /* check right assoc */
13300 if (prev_prec == prec && is_right_associativity(prec))
13301 break;
13302 }
13303 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13304 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013305 }
13306 if (op == TOK_RPAREN) {
13307 goto err;
13308 }
13309 }
13310
13311 /* Push this operator to the stack and remember it. */
13312 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013313 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013314 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013315 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013316}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013317#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013318
13319
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013320/* ============ main() and helpers */
13321
13322/*
13323 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013324 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013325static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013326static void
13327exitshell(void)
13328{
13329 struct jmploc loc;
13330 char *p;
13331 int status;
13332
13333 status = exitstatus;
13334 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13335 if (setjmp(loc.loc)) {
13336 if (exception == EXEXIT)
13337/* dash bug: it just does _exit(exitstatus) here
13338 * but we have to do setjobctl(0) first!
13339 * (bug is still not fixed in dash-0.5.3 - if you run dash
13340 * under Midnight Commander, on exit from dash MC is backgrounded) */
13341 status = exitstatus;
13342 goto out;
13343 }
13344 exception_handler = &loc;
13345 p = trap[0];
13346 if (p) {
13347 trap[0] = NULL;
13348 evalstring(p, 0);
13349 }
13350 flush_stdout_stderr();
13351 out:
13352 setjobctl(0);
13353 _exit(status);
13354 /* NOTREACHED */
13355}
13356
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013357static void
13358init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013359{
13360 /* from input.c: */
13361 basepf.nextc = basepf.buf = basebuf;
13362
13363 /* from trap.c: */
13364 signal(SIGCHLD, SIG_DFL);
13365
13366 /* from var.c: */
13367 {
13368 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013369 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013370 const char *p;
13371 struct stat st1, st2;
13372
13373 initvar();
13374 for (envp = environ; envp && *envp; envp++) {
13375 if (strchr(*envp, '=')) {
13376 setvareq(*envp, VEXPORT|VTEXTFIXED);
13377 }
13378 }
13379
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013380 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013381 setvar("PPID", ppid, 0);
13382
13383 p = lookupvar("PWD");
13384 if (p)
13385 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13386 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13387 p = '\0';
13388 setpwd(p, 0);
13389 }
13390}
13391
13392/*
13393 * Process the shell command line arguments.
13394 */
13395static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013396procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013397{
13398 int i;
13399 const char *xminusc;
13400 char **xargv;
13401
13402 xargv = argv;
13403 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013404 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013405 xargv++;
13406 for (i = 0; i < NOPTS; i++)
13407 optlist[i] = 2;
13408 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013409 if (options(1)) {
13410 /* it already printed err message */
13411 raise_exception(EXERROR);
13412 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013413 xargv = argptr;
13414 xminusc = minusc;
13415 if (*xargv == NULL) {
13416 if (xminusc)
13417 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13418 sflag = 1;
13419 }
13420 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13421 iflag = 1;
13422 if (mflag == 2)
13423 mflag = iflag;
13424 for (i = 0; i < NOPTS; i++)
13425 if (optlist[i] == 2)
13426 optlist[i] = 0;
13427#if DEBUG == 2
13428 debug = 1;
13429#endif
13430 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13431 if (xminusc) {
13432 minusc = *xargv++;
13433 if (*xargv)
13434 goto setarg0;
13435 } else if (!sflag) {
13436 setinputfile(*xargv, 0);
13437 setarg0:
13438 arg0 = *xargv++;
13439 commandname = arg0;
13440 }
13441
13442 shellparam.p = xargv;
13443#if ENABLE_ASH_GETOPTS
13444 shellparam.optind = 1;
13445 shellparam.optoff = -1;
13446#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013447 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013448 while (*xargv) {
13449 shellparam.nparam++;
13450 xargv++;
13451 }
13452 optschanged();
13453}
13454
13455/*
13456 * Read /etc/profile or .profile.
13457 */
13458static void
13459read_profile(const char *name)
13460{
13461 int skip;
13462
13463 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13464 return;
13465 skip = cmdloop(0);
13466 popfile();
13467 if (skip)
13468 exitshell();
13469}
13470
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013471/*
13472 * This routine is called when an error or an interrupt occurs in an
13473 * interactive shell and control is returned to the main command loop.
13474 */
13475static void
13476reset(void)
13477{
13478 /* from eval.c: */
13479 evalskip = 0;
13480 loopnest = 0;
13481 /* from input.c: */
13482 parselleft = parsenleft = 0; /* clear input buffer */
13483 popallfiles();
13484 /* from parser.c: */
13485 tokpushback = 0;
13486 checkkwd = 0;
13487 /* from redir.c: */
13488 clearredir(0);
13489}
13490
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013491#if PROFILE
13492static short profile_buf[16384];
13493extern int etext();
13494#endif
13495
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013496/*
13497 * Main routine. We initialize things, parse the arguments, execute
13498 * profiles if we're a login shell, and then call cmdloop to execute
13499 * commands. The setjmp call sets up the location to jump to when an
13500 * exception occurs. When an exception occurs the variable "state"
13501 * is used to figure out how far we had gotten.
13502 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013503int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013504int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013505{
13506 char *shinit;
13507 volatile int state;
13508 struct jmploc jmploc;
13509 struct stackmark smark;
13510
Denis Vlasenko01631112007-12-16 17:20:38 +000013511 /* Initialize global data */
13512 INIT_G_misc();
13513 INIT_G_memstack();
13514 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013515#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013516 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013517#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013518 INIT_G_cmdtable();
13519
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013520#if PROFILE
13521 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13522#endif
13523
13524#if ENABLE_FEATURE_EDITING
13525 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13526#endif
13527 state = 0;
13528 if (setjmp(jmploc.loc)) {
13529 int e;
13530 int s;
13531
13532 reset();
13533
13534 e = exception;
13535 if (e == EXERROR)
13536 exitstatus = 2;
13537 s = state;
13538 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13539 exitshell();
13540
13541 if (e == EXINT) {
13542 outcslow('\n', stderr);
13543 }
13544 popstackmark(&smark);
13545 FORCE_INT_ON; /* enable interrupts */
13546 if (s == 1)
13547 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013548 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013549 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013550 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013551 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013552 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013553 }
13554 exception_handler = &jmploc;
13555#if DEBUG
13556 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013557 trace_puts("Shell args: ");
13558 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013559#endif
13560 rootpid = getpid();
13561
13562#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013563 /* Can use monotonic_ns() for better randomness but for now it is
13564 * not used anywhere else in busybox... so avoid bloat */
13565 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013566#endif
13567 init();
13568 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013569 procargs(argv);
13570
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013571#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13572 if (iflag) {
13573 const char *hp = lookupvar("HISTFILE");
13574
13575 if (hp == NULL) {
13576 hp = lookupvar("HOME");
13577 if (hp != NULL) {
13578 char *defhp = concat_path_file(hp, ".ash_history");
13579 setvar("HISTFILE", defhp, 0);
13580 free(defhp);
13581 }
13582 }
13583 }
13584#endif
13585 if (argv[0] && argv[0][0] == '-')
13586 isloginsh = 1;
13587 if (isloginsh) {
13588 state = 1;
13589 read_profile("/etc/profile");
13590 state1:
13591 state = 2;
13592 read_profile(".profile");
13593 }
13594 state2:
13595 state = 3;
13596 if (
13597#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013598 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013599#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013600 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013601 ) {
13602 shinit = lookupvar("ENV");
13603 if (shinit != NULL && *shinit != '\0') {
13604 read_profile(shinit);
13605 }
13606 }
13607 state3:
13608 state = 4;
13609 if (minusc)
13610 evalstring(minusc, 0);
13611
13612 if (sflag || minusc == NULL) {
13613#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013614 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013615 const char *hp = lookupvar("HISTFILE");
13616
13617 if (hp != NULL)
13618 line_input_state->hist_file = hp;
13619 }
13620#endif
13621 state4: /* XXX ??? - why isn't this before the "if" statement */
13622 cmdloop(1);
13623 }
13624#if PROFILE
13625 monitor(0);
13626#endif
13627#ifdef GPROF
13628 {
13629 extern void _mcleanup(void);
13630 _mcleanup();
13631 }
13632#endif
13633 exitshell();
13634 /* NOTREACHED */
13635}
13636
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013637#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013638const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013639int main(int argc, char **argv)
13640{
13641 return ash_main(argc, argv);
13642}
13643#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013644
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013645
Eric Andersendf82f612001-06-28 07:46:40 +000013646/*-
13647 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013648 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013649 *
13650 * This code is derived from software contributed to Berkeley by
13651 * Kenneth Almquist.
13652 *
13653 * Redistribution and use in source and binary forms, with or without
13654 * modification, are permitted provided that the following conditions
13655 * are met:
13656 * 1. Redistributions of source code must retain the above copyright
13657 * notice, this list of conditions and the following disclaimer.
13658 * 2. Redistributions in binary form must reproduce the above copyright
13659 * notice, this list of conditions and the following disclaimer in the
13660 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013661 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013662 * may be used to endorse or promote products derived from this software
13663 * without specific prior written permission.
13664 *
13665 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13666 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13667 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13668 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13669 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13670 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13671 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13672 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13673 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13674 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13675 * SUCH DAMAGE.
13676 */