blob: 962d5be02e403f0e06d974f1d4b3bb5a957ec396 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
Eric Andersencb57d552001-06-28 07:25:16 +000011 * This code is derived from software contributed to Berkeley by
12 * Kenneth Almquist.
13 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000014 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000015 *
Eric Andersen81fe1232003-07-29 06:38:40 +000016 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Eric Andersen90898442003-08-06 11:20:52 +000020 * rewrite arith.y to micro stack based cryptic algorithm by
21 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
22 *
Eric Andersenef02f822004-03-11 13:34:24 +000023 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
24 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000025 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000026 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000027 * used in busybox and size optimizations,
28 * rewrote arith (see notes to this), added locale support,
29 * rewrote dynamic variables.
Eric Andersen90898442003-08-06 11:20:52 +000030 */
31
Eric Andersen90898442003-08-06 11:20:52 +000032/*
Eric Andersenc470f442003-07-28 09:56:35 +000033 * The follow should be set to reflect the type of system you have:
34 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
35 * define SYSV if you are running under System V.
36 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
37 * define DEBUG=2 to compile in and turn on debugging.
38 *
39 * When debugging is on, debugging info will be written to ./trace and
40 * a quit signal will generate a core dump.
41 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000042#define DEBUG 0
Eric Andersenc470f442003-07-28 09:56:35 +000043#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000044
45#define IFS_BROKEN
46
47#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048
Denis Vlasenkob012b102007-02-19 22:43:01 +000049#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000050#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#define _GNU_SOURCE
52#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000053#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000054
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000055#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000056#include <paths.h>
57#include <setjmp.h>
58#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000059#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000060#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000061#endif
62
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000063#ifndef PIPE_BUF
64#define PIPE_BUF 4096 /* amount of buffering in a pipe */
65#endif
66
Denis Vlasenkob012b102007-02-19 22:43:01 +000067#if defined(__uClinux__)
68#error "Do not even bother, ash will not run on uClinux"
69#endif
70
Denis Vlasenkob012b102007-02-19 22:43:01 +000071
Denis Vlasenko01631112007-12-16 17:20:38 +000072/* ============ Hash table sizes. Configurable. */
73
74#define VTABSIZE 39
75#define ATABSIZE 39
76#define CMDTABLESIZE 31 /* should be prime */
77
78
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000079/* ============ Misc helpers */
80
81#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
82
83/* C99 say: "char" declaration may be signed or unsigned default */
84#define signed_char2int(sc) ((int)((signed char)sc))
85
86
Denis Vlasenkob012b102007-02-19 22:43:01 +000087/* ============ Shell options */
88
89static const char *const optletters_optnames[] = {
90 "e" "errexit",
91 "f" "noglob",
92 "I" "ignoreeof",
93 "i" "interactive",
94 "m" "monitor",
95 "n" "noexec",
96 "s" "stdin",
97 "x" "xtrace",
98 "v" "verbose",
99 "C" "noclobber",
100 "a" "allexport",
101 "b" "notify",
102 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000103 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000104#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000105 ,"\0" "nolog"
106 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000107#endif
108};
109
110#define optletters(n) optletters_optnames[(n)][0]
111#define optnames(n) (&optletters_optnames[(n)][1])
112
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000113enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114
Eric Andersenc470f442003-07-28 09:56:35 +0000115
Denis Vlasenkob012b102007-02-19 22:43:01 +0000116/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000117
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000118static const char homestr[] ALIGN1 = "HOME";
119static const char snlfmt[] ALIGN1 = "%s\n";
120static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000121
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000122/*
Eric Andersenc470f442003-07-28 09:56:35 +0000123 * We enclose jmp_buf in a structure so that we can declare pointers to
124 * jump locations. The global variable handler contains the location to
125 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000126 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000127 * exception handlers, the user should save the value of handler on entry
128 * to an inner scope, set handler to point to a jmploc structure for the
129 * inner scope, and restore handler on exit from the scope.
130 */
Eric Andersenc470f442003-07-28 09:56:35 +0000131struct jmploc {
132 jmp_buf loc;
133};
Denis Vlasenko01631112007-12-16 17:20:38 +0000134
135struct globals_misc {
136 /* pid of main shell */
137 int rootpid;
138 /* shell level: 0 for the main shell, 1 for its children, and so on */
139 int shlvl;
140#define rootshell (!shlvl)
141 char *minusc; /* argument to -c option */
142
143 char *curdir; // = nullstr; /* current working directory */
144 char *physdir; // = nullstr; /* physical working directory */
145
146 char *arg0; /* value of $0 */
147
148 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000149
150// disabled by vda: cannot understand how it was supposed to work -
151// cannot fix bugs. That's why you have to explain your non-trivial designs!
152// /* do we generate EXSIG events */
153// int exsig; /* counter */
154 volatile int suppressint; /* counter */
155 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
156 /* last pending signal */
157 volatile /*sig_atomic_t*/ smallint pendingsig;
158 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000159 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000160#define EXINT 0 /* SIGINT received */
161#define EXERROR 1 /* a generic error */
162#define EXSHELLPROC 2 /* execute a shell procedure */
163#define EXEXEC 3 /* command execution failed */
164#define EXEXIT 4 /* exit the shell */
165#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000166
Denis Vlasenko01631112007-12-16 17:20:38 +0000167 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000168 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000169
170 char optlist[NOPTS];
171#define eflag optlist[0]
172#define fflag optlist[1]
173#define Iflag optlist[2]
174#define iflag optlist[3]
175#define mflag optlist[4]
176#define nflag optlist[5]
177#define sflag optlist[6]
178#define xflag optlist[7]
179#define vflag optlist[8]
180#define Cflag optlist[9]
181#define aflag optlist[10]
182#define bflag optlist[11]
183#define uflag optlist[12]
184#define viflag optlist[13]
185#if DEBUG
186#define nolog optlist[14]
187#define debug optlist[15]
188#endif
189
190 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000191 /*
192 * Sigmode records the current value of the signal handlers for the various
193 * modes. A value of zero means that the current handler is not known.
194 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
195 */
196 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000197#define S_DFL 1 /* default signal handling (SIG_DFL) */
198#define S_CATCH 2 /* signal is caught */
199#define S_IGN 3 /* signal is ignored (SIG_IGN) */
200#define S_HARD_IGN 4 /* signal is ignored permenantly */
201#define S_RESET 5 /* temporary - to reset a hard ignored sig */
202
Denis Vlasenko01631112007-12-16 17:20:38 +0000203 /* indicates specified signal received */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +0000204 char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000205 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000206
207 /* Rarely referenced stuff */
208#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000209 /* Random number generators */
210 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
211 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000212#endif
213 pid_t backgndpid; /* pid of last background process */
214 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000215};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000216extern struct globals_misc *const ash_ptr_to_globals_misc;
217#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000218#define rootpid (G_misc.rootpid )
219#define shlvl (G_misc.shlvl )
220#define minusc (G_misc.minusc )
221#define curdir (G_misc.curdir )
222#define physdir (G_misc.physdir )
223#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000224#define exception_handler (G_misc.exception_handler)
225#define exception (G_misc.exception )
226#define suppressint (G_misc.suppressint )
227#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000228//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000229#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000230#define isloginsh (G_misc.isloginsh )
231#define nullstr (G_misc.nullstr )
232#define optlist (G_misc.optlist )
233#define sigmode (G_misc.sigmode )
234#define gotsig (G_misc.gotsig )
235#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000236#define random_galois_LFSR (G_misc.random_galois_LFSR)
237#define random_LCG (G_misc.random_LCG )
238#define backgndpid (G_misc.backgndpid )
239#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000240#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000241 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
242 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000243 curdir = nullstr; \
244 physdir = nullstr; \
245} while (0)
246
247
Denis Vlasenko559691a2008-10-05 18:39:31 +0000248/* ============ Utility functions */
249static int isdigit_str9(const char *str)
250{
251 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
252 while (--maxlen && isdigit(*str))
253 str++;
254 return (*str == '\0');
255}
Denis Vlasenko01631112007-12-16 17:20:38 +0000256
Denis Vlasenko559691a2008-10-05 18:39:31 +0000257
258/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000259/*
Eric Andersen2870d962001-07-02 17:27:21 +0000260 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000261 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000262 * much more efficient and portable. (But hacking the kernel is so much
263 * more fun than worrying about efficiency and portability. :-))
264 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000265#define INT_OFF do { \
266 suppressint++; \
267 xbarrier(); \
268} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000269
270/*
271 * Called to raise an exception. Since C doesn't include exceptions, we
272 * just do a longjmp to the exception handler. The type of exception is
273 * stored in the global variable "exception".
274 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000275static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000276static void
277raise_exception(int e)
278{
279#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000280 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000281 abort();
282#endif
283 INT_OFF;
284 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000285 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000286}
287
288/*
289 * Called from trap.c when a SIGINT is received. (If the user specifies
290 * that SIGINT is to be trapped or ignored using the trap builtin, then
291 * this routine is not called.) Suppressint is nonzero when interrupts
292 * are held using the INT_OFF macro. (The test for iflag is just
293 * defensive programming.)
294 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000295static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000296static void
297raise_interrupt(void)
298{
299 int i;
300
301 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000302 /* Signal is not automatically unmasked after it is raised,
303 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000304 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000305 /* pendingsig = 0; - now done in onsig() */
306
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307 i = EXSIG;
308 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
309 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000310 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000311 signal(SIGINT, SIG_DFL);
312 raise(SIGINT);
313 }
314 i = EXINT;
315 }
316 raise_exception(i);
317 /* NOTREACHED */
318}
319
320#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000321static void
322int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323{
324 if (--suppressint == 0 && intpending) {
325 raise_interrupt();
326 }
327}
328#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000329static void
330force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000331{
332 suppressint = 0;
333 if (intpending)
334 raise_interrupt();
335}
336#define FORCE_INT_ON force_int_on()
337#else
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000338#define INT_ON do { \
339 xbarrier(); \
340 if (--suppressint == 0 && intpending) \
341 raise_interrupt(); \
342} while (0)
343#define FORCE_INT_ON do { \
344 xbarrier(); \
345 suppressint = 0; \
346 if (intpending) \
347 raise_interrupt(); \
348} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000349#endif /* ASH_OPTIMIZE_FOR_SIZE */
350
351#define SAVE_INT(v) ((v) = suppressint)
352
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000353#define RESTORE_INT(v) do { \
354 xbarrier(); \
355 suppressint = (v); \
356 if (suppressint == 0 && intpending) \
357 raise_interrupt(); \
358} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000359
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000360/*
361 * Ignore a signal. Only one usage site - in forkchild()
362 */
363static void
364ignoresig(int signo)
365{
366 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
367 signal(signo, SIG_IGN);
368 }
369 sigmode[signo - 1] = S_HARD_IGN;
370}
371
372/*
373 * Signal handler. Only one usage site - in setsignal()
374 */
375static void
376onsig(int signo)
377{
378 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000379 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000380
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +0000381 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000382 if (!suppressint) {
383 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000384 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000385 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000386 intpending = 1;
387 }
388}
389
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000390
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000391/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000392
Eric Andersenc470f442003-07-28 09:56:35 +0000393static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000394outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000395{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000396 INT_OFF;
397 fputs(p, file);
398 INT_ON;
399}
400
401static void
402flush_stdout_stderr(void)
403{
404 INT_OFF;
405 fflush(stdout);
406 fflush(stderr);
407 INT_ON;
408}
409
410static void
411flush_stderr(void)
412{
413 INT_OFF;
414 fflush(stderr);
415 INT_ON;
416}
417
418static void
419outcslow(int c, FILE *dest)
420{
421 INT_OFF;
422 putc(c, dest);
423 fflush(dest);
424 INT_ON;
425}
426
427static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
428static int
429out1fmt(const char *fmt, ...)
430{
431 va_list ap;
432 int r;
433
434 INT_OFF;
435 va_start(ap, fmt);
436 r = vprintf(fmt, ap);
437 va_end(ap);
438 INT_ON;
439 return r;
440}
441
442static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
443static int
444fmtstr(char *outbuf, size_t length, const char *fmt, ...)
445{
446 va_list ap;
447 int ret;
448
449 va_start(ap, fmt);
450 INT_OFF;
451 ret = vsnprintf(outbuf, length, fmt, ap);
452 va_end(ap);
453 INT_ON;
454 return ret;
455}
456
457static void
458out1str(const char *p)
459{
460 outstr(p, stdout);
461}
462
463static void
464out2str(const char *p)
465{
466 outstr(p, stderr);
467 flush_stderr();
468}
469
470
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000471/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000472
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000473/* control characters in argument strings */
474#define CTLESC '\201' /* escape next character */
475#define CTLVAR '\202' /* variable defn */
476#define CTLENDVAR '\203'
477#define CTLBACKQ '\204'
478#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
479/* CTLBACKQ | CTLQUOTE == '\205' */
480#define CTLARI '\206' /* arithmetic expression */
481#define CTLENDARI '\207'
482#define CTLQUOTEMARK '\210'
483
484/* variable substitution byte (follows CTLVAR) */
485#define VSTYPE 0x0f /* type of variable substitution */
486#define VSNUL 0x10 /* colon--treat the empty string as unset */
487#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
488
489/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000490#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
491#define VSMINUS 0x2 /* ${var-text} */
492#define VSPLUS 0x3 /* ${var+text} */
493#define VSQUESTION 0x4 /* ${var?message} */
494#define VSASSIGN 0x5 /* ${var=text} */
495#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
496#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
497#define VSTRIMLEFT 0x8 /* ${var#pattern} */
498#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
499#define VSLENGTH 0xa /* ${#var} */
500#if ENABLE_ASH_BASH_COMPAT
501#define VSSUBSTR 0xc /* ${var:position:length} */
502#define VSREPLACE 0xd /* ${var/pattern/replacement} */
503#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
504#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000505
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000506static const char dolatstr[] ALIGN1 = {
507 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
508};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000509
Denis Vlasenko559691a2008-10-05 18:39:31 +0000510#define NCMD 0
511#define NPIPE 1
512#define NREDIR 2
513#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000514#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000515#define NAND 5
516#define NOR 6
517#define NSEMI 7
518#define NIF 8
519#define NWHILE 9
520#define NUNTIL 10
521#define NFOR 11
522#define NCASE 12
523#define NCLIST 13
524#define NDEFUN 14
525#define NARG 15
526#define NTO 16
527#if ENABLE_ASH_BASH_COMPAT
528#define NTO2 17
529#endif
530#define NCLOBBER 18
531#define NFROM 19
532#define NFROMTO 20
533#define NAPPEND 21
534#define NTOFD 22
535#define NFROMFD 23
536#define NHERE 24
537#define NXHERE 25
538#define NNOT 26
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000539
540union node;
541
542struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000543 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000544 union node *assign;
545 union node *args;
546 union node *redirect;
547};
548
549struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000550 smallint type;
551 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000552 struct nodelist *cmdlist;
553};
554
555struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000556 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000557 union node *n;
558 union node *redirect;
559};
560
561struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000562 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000563 union node *ch1;
564 union node *ch2;
565};
566
567struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000568 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000569 union node *test;
570 union node *ifpart;
571 union node *elsepart;
572};
573
574struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000575 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000576 union node *args;
577 union node *body;
578 char *var;
579};
580
581struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000582 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000583 union node *expr;
584 union node *cases;
585};
586
587struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000588 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000589 union node *next;
590 union node *pattern;
591 union node *body;
592};
593
594struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000595 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000596 union node *next;
597 char *text;
598 struct nodelist *backquote;
599};
600
Denis Vlasenko559691a2008-10-05 18:39:31 +0000601/* nfile and ndup layout must match!
602 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
603 * that it is actually NTO2 (>&file), and change its type.
604 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000605struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000606 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000607 union node *next;
608 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000609 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000610 union node *fname;
611 char *expfname;
612};
613
614struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000615 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000616 union node *next;
617 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000618 int dupfd;
619 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621};
622
623struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000624 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000625 union node *next;
626 int fd;
627 union node *doc;
628};
629
630struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000631 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000632 union node *com;
633};
634
635union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000636 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000637 struct ncmd ncmd;
638 struct npipe npipe;
639 struct nredir nredir;
640 struct nbinary nbinary;
641 struct nif nif;
642 struct nfor nfor;
643 struct ncase ncase;
644 struct nclist nclist;
645 struct narg narg;
646 struct nfile nfile;
647 struct ndup ndup;
648 struct nhere nhere;
649 struct nnot nnot;
650};
651
652struct nodelist {
653 struct nodelist *next;
654 union node *n;
655};
656
657struct funcnode {
658 int count;
659 union node n;
660};
661
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000662/*
663 * Free a parse tree.
664 */
665static void
666freefunc(struct funcnode *f)
667{
668 if (f && --f->count < 0)
669 free(f);
670}
671
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000672
673/* ============ Debugging output */
674
675#if DEBUG
676
677static FILE *tracefile;
678
679static void
680trace_printf(const char *fmt, ...)
681{
682 va_list va;
683
684 if (debug != 1)
685 return;
686 va_start(va, fmt);
687 vfprintf(tracefile, fmt, va);
688 va_end(va);
689}
690
691static void
692trace_vprintf(const char *fmt, va_list va)
693{
694 if (debug != 1)
695 return;
696 vfprintf(tracefile, fmt, va);
697}
698
699static void
700trace_puts(const char *s)
701{
702 if (debug != 1)
703 return;
704 fputs(s, tracefile);
705}
706
707static void
708trace_puts_quoted(char *s)
709{
710 char *p;
711 char c;
712
713 if (debug != 1)
714 return;
715 putc('"', tracefile);
716 for (p = s; *p; p++) {
717 switch (*p) {
718 case '\n': c = 'n'; goto backslash;
719 case '\t': c = 't'; goto backslash;
720 case '\r': c = 'r'; goto backslash;
721 case '"': c = '"'; goto backslash;
722 case '\\': c = '\\'; goto backslash;
723 case CTLESC: c = 'e'; goto backslash;
724 case CTLVAR: c = 'v'; goto backslash;
725 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
726 case CTLBACKQ: c = 'q'; goto backslash;
727 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
728 backslash:
729 putc('\\', tracefile);
730 putc(c, tracefile);
731 break;
732 default:
733 if (*p >= ' ' && *p <= '~')
734 putc(*p, tracefile);
735 else {
736 putc('\\', tracefile);
737 putc(*p >> 6 & 03, tracefile);
738 putc(*p >> 3 & 07, tracefile);
739 putc(*p & 07, tracefile);
740 }
741 break;
742 }
743 }
744 putc('"', tracefile);
745}
746
747static void
748trace_puts_args(char **ap)
749{
750 if (debug != 1)
751 return;
752 if (!*ap)
753 return;
754 while (1) {
755 trace_puts_quoted(*ap);
756 if (!*++ap) {
757 putc('\n', tracefile);
758 break;
759 }
760 putc(' ', tracefile);
761 }
762}
763
764static void
765opentrace(void)
766{
767 char s[100];
768#ifdef O_APPEND
769 int flags;
770#endif
771
772 if (debug != 1) {
773 if (tracefile)
774 fflush(tracefile);
775 /* leave open because libedit might be using it */
776 return;
777 }
778 strcpy(s, "./trace");
779 if (tracefile) {
780 if (!freopen(s, "a", tracefile)) {
781 fprintf(stderr, "Can't re-open %s\n", s);
782 debug = 0;
783 return;
784 }
785 } else {
786 tracefile = fopen(s, "a");
787 if (tracefile == NULL) {
788 fprintf(stderr, "Can't open %s\n", s);
789 debug = 0;
790 return;
791 }
792 }
793#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000794 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000795 if (flags >= 0)
796 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
797#endif
798 setlinebuf(tracefile);
799 fputs("\nTracing started.\n", tracefile);
800}
801
802static void
803indent(int amount, char *pfx, FILE *fp)
804{
805 int i;
806
807 for (i = 0; i < amount; i++) {
808 if (pfx && i == amount - 1)
809 fputs(pfx, fp);
810 putc('\t', fp);
811 }
812}
813
814/* little circular references here... */
815static void shtree(union node *n, int ind, char *pfx, FILE *fp);
816
817static void
818sharg(union node *arg, FILE *fp)
819{
820 char *p;
821 struct nodelist *bqlist;
822 int subtype;
823
824 if (arg->type != NARG) {
825 out1fmt("<node type %d>\n", arg->type);
826 abort();
827 }
828 bqlist = arg->narg.backquote;
829 for (p = arg->narg.text; *p; p++) {
830 switch (*p) {
831 case CTLESC:
832 putc(*++p, fp);
833 break;
834 case CTLVAR:
835 putc('$', fp);
836 putc('{', fp);
837 subtype = *++p;
838 if (subtype == VSLENGTH)
839 putc('#', fp);
840
841 while (*p != '=')
842 putc(*p++, fp);
843
844 if (subtype & VSNUL)
845 putc(':', fp);
846
847 switch (subtype & VSTYPE) {
848 case VSNORMAL:
849 putc('}', fp);
850 break;
851 case VSMINUS:
852 putc('-', fp);
853 break;
854 case VSPLUS:
855 putc('+', fp);
856 break;
857 case VSQUESTION:
858 putc('?', fp);
859 break;
860 case VSASSIGN:
861 putc('=', fp);
862 break;
863 case VSTRIMLEFT:
864 putc('#', fp);
865 break;
866 case VSTRIMLEFTMAX:
867 putc('#', fp);
868 putc('#', fp);
869 break;
870 case VSTRIMRIGHT:
871 putc('%', fp);
872 break;
873 case VSTRIMRIGHTMAX:
874 putc('%', fp);
875 putc('%', fp);
876 break;
877 case VSLENGTH:
878 break;
879 default:
880 out1fmt("<subtype %d>", subtype);
881 }
882 break;
883 case CTLENDVAR:
884 putc('}', fp);
885 break;
886 case CTLBACKQ:
887 case CTLBACKQ|CTLQUOTE:
888 putc('$', fp);
889 putc('(', fp);
890 shtree(bqlist->n, -1, NULL, fp);
891 putc(')', fp);
892 break;
893 default:
894 putc(*p, fp);
895 break;
896 }
897 }
898}
899
900static void
901shcmd(union node *cmd, FILE *fp)
902{
903 union node *np;
904 int first;
905 const char *s;
906 int dftfd;
907
908 first = 1;
909 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000910 if (!first)
911 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000912 sharg(np, fp);
913 first = 0;
914 }
915 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000916 if (!first)
917 putc(' ', fp);
918 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000919 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000920 case NTO: s = ">>"+1; dftfd = 1; break;
921 case NCLOBBER: s = ">|"; dftfd = 1; break;
922 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000923#if ENABLE_ASH_BASH_COMPAT
924 case NTO2:
925#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000926 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000927 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000928 case NFROMFD: s = "<&"; break;
929 case NFROMTO: s = "<>"; break;
930 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000931 }
932 if (np->nfile.fd != dftfd)
933 fprintf(fp, "%d", np->nfile.fd);
934 fputs(s, fp);
935 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
936 fprintf(fp, "%d", np->ndup.dupfd);
937 } else {
938 sharg(np->nfile.fname, fp);
939 }
940 first = 0;
941 }
942}
943
944static void
945shtree(union node *n, int ind, char *pfx, FILE *fp)
946{
947 struct nodelist *lp;
948 const char *s;
949
950 if (n == NULL)
951 return;
952
953 indent(ind, pfx, fp);
954 switch (n->type) {
955 case NSEMI:
956 s = "; ";
957 goto binop;
958 case NAND:
959 s = " && ";
960 goto binop;
961 case NOR:
962 s = " || ";
963 binop:
964 shtree(n->nbinary.ch1, ind, NULL, fp);
965 /* if (ind < 0) */
966 fputs(s, fp);
967 shtree(n->nbinary.ch2, ind, NULL, fp);
968 break;
969 case NCMD:
970 shcmd(n, fp);
971 if (ind >= 0)
972 putc('\n', fp);
973 break;
974 case NPIPE:
975 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
976 shcmd(lp->n, fp);
977 if (lp->next)
978 fputs(" | ", fp);
979 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000980 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000981 fputs(" &", fp);
982 if (ind >= 0)
983 putc('\n', fp);
984 break;
985 default:
986 fprintf(fp, "<node type %d>", n->type);
987 if (ind >= 0)
988 putc('\n', fp);
989 break;
990 }
991}
992
993static void
994showtree(union node *n)
995{
996 trace_puts("showtree called\n");
997 shtree(n, 1, NULL, stdout);
998}
999
1000#define TRACE(param) trace_printf param
1001#define TRACEV(param) trace_vprintf param
1002
1003#else
1004
1005#define TRACE(param)
1006#define TRACEV(param)
1007
1008#endif /* DEBUG */
1009
1010
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001011/* ============ Parser data */
1012
1013/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001014 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1015 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001016struct strlist {
1017 struct strlist *next;
1018 char *text;
1019};
1020
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001021struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001022
Denis Vlasenkob012b102007-02-19 22:43:01 +00001023struct strpush {
1024 struct strpush *prev; /* preceding string on stack */
1025 char *prevstring;
1026 int prevnleft;
1027#if ENABLE_ASH_ALIAS
1028 struct alias *ap; /* if push was associated with an alias */
1029#endif
1030 char *string; /* remember the string since it may change */
1031};
1032
1033struct parsefile {
1034 struct parsefile *prev; /* preceding file on stack */
1035 int linno; /* current line */
1036 int fd; /* file descriptor (or -1 if string) */
1037 int nleft; /* number of chars left in this line */
1038 int lleft; /* number of chars left in this buffer */
1039 char *nextc; /* next char in buffer */
1040 char *buf; /* input buffer */
1041 struct strpush *strpush; /* for pushing strings at this level */
1042 struct strpush basestrpush; /* so pushing one is fast */
1043};
1044
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001045static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001046static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001047static int startlinno; /* line # where last token started */
1048static char *commandname; /* currently executing command */
1049static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001050static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001051
1052
1053/* ============ Message printing */
1054
1055static void
1056ash_vmsg(const char *msg, va_list ap)
1057{
1058 fprintf(stderr, "%s: ", arg0);
1059 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001060 if (strcmp(arg0, commandname))
1061 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001062 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001063 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001064 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001065 vfprintf(stderr, msg, ap);
1066 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001067}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001068
1069/*
1070 * Exverror is called to raise the error exception. If the second argument
1071 * is not NULL then error prints an error message using printf style
1072 * formatting. It then raises the error exception.
1073 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001074static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001075static void
1076ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001077{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001078#if DEBUG
1079 if (msg) {
1080 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1081 TRACEV((msg, ap));
1082 TRACE(("\") pid=%d\n", getpid()));
1083 } else
1084 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1085 if (msg)
1086#endif
1087 ash_vmsg(msg, ap);
1088
1089 flush_stdout_stderr();
1090 raise_exception(cond);
1091 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001092}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001093
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001094static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001095static void
1096ash_msg_and_raise_error(const char *msg, ...)
1097{
1098 va_list ap;
1099
1100 va_start(ap, msg);
1101 ash_vmsg_and_raise(EXERROR, msg, ap);
1102 /* NOTREACHED */
1103 va_end(ap);
1104}
1105
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001106static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001107static void
1108ash_msg_and_raise(int cond, const char *msg, ...)
1109{
1110 va_list ap;
1111
1112 va_start(ap, msg);
1113 ash_vmsg_and_raise(cond, msg, ap);
1114 /* NOTREACHED */
1115 va_end(ap);
1116}
1117
1118/*
1119 * error/warning routines for external builtins
1120 */
1121static void
1122ash_msg(const char *fmt, ...)
1123{
1124 va_list ap;
1125
1126 va_start(ap, fmt);
1127 ash_vmsg(fmt, ap);
1128 va_end(ap);
1129}
1130
1131/*
1132 * Return a string describing an error. The returned string may be a
1133 * pointer to a static buffer that will be overwritten on the next call.
1134 * Action describes the operation that got the error.
1135 */
1136static const char *
1137errmsg(int e, const char *em)
1138{
1139 if (e == ENOENT || e == ENOTDIR) {
1140 return em;
1141 }
1142 return strerror(e);
1143}
1144
1145
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001146/* ============ Memory allocation */
1147
1148/*
1149 * It appears that grabstackstr() will barf with such alignments
1150 * because stalloc() will return a string allocated in a new stackblock.
1151 */
1152#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1153enum {
1154 /* Most machines require the value returned from malloc to be aligned
1155 * in some way. The following macro will get this right
1156 * on many machines. */
1157 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1158 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001159 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001160};
1161
1162struct stack_block {
1163 struct stack_block *prev;
1164 char space[MINSIZE];
1165};
1166
1167struct stackmark {
1168 struct stack_block *stackp;
1169 char *stacknxt;
1170 size_t stacknleft;
1171 struct stackmark *marknext;
1172};
1173
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001174
Denis Vlasenko01631112007-12-16 17:20:38 +00001175struct globals_memstack {
1176 struct stack_block *g_stackp; // = &stackbase;
1177 struct stackmark *markp;
1178 char *g_stacknxt; // = stackbase.space;
1179 char *sstrend; // = stackbase.space + MINSIZE;
1180 size_t g_stacknleft; // = MINSIZE;
1181 int herefd; // = -1;
1182 struct stack_block stackbase;
1183};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001184extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1185#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001186#define g_stackp (G_memstack.g_stackp )
1187#define markp (G_memstack.markp )
1188#define g_stacknxt (G_memstack.g_stacknxt )
1189#define sstrend (G_memstack.sstrend )
1190#define g_stacknleft (G_memstack.g_stacknleft)
1191#define herefd (G_memstack.herefd )
1192#define stackbase (G_memstack.stackbase )
1193#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001194 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1195 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001196 g_stackp = &stackbase; \
1197 g_stacknxt = stackbase.space; \
1198 g_stacknleft = MINSIZE; \
1199 sstrend = stackbase.space + MINSIZE; \
1200 herefd = -1; \
1201} while (0)
1202
1203#define stackblock() ((void *)g_stacknxt)
1204#define stackblocksize() g_stacknleft
1205
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001206
1207static void *
1208ckrealloc(void * p, size_t nbytes)
1209{
1210 p = realloc(p, nbytes);
1211 if (!p)
1212 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1213 return p;
1214}
1215
1216static void *
1217ckmalloc(size_t nbytes)
1218{
1219 return ckrealloc(NULL, nbytes);
1220}
1221
Denis Vlasenko597906c2008-02-20 16:38:54 +00001222static void *
1223ckzalloc(size_t nbytes)
1224{
1225 return memset(ckmalloc(nbytes), 0, nbytes);
1226}
1227
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001228/*
1229 * Make a copy of a string in safe storage.
1230 */
1231static char *
1232ckstrdup(const char *s)
1233{
1234 char *p = strdup(s);
1235 if (!p)
1236 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1237 return p;
1238}
1239
1240/*
1241 * Parse trees for commands are allocated in lifo order, so we use a stack
1242 * to make this more efficient, and also to avoid all sorts of exception
1243 * handling code to handle interrupts in the middle of a parse.
1244 *
1245 * The size 504 was chosen because the Ultrix malloc handles that size
1246 * well.
1247 */
1248static void *
1249stalloc(size_t nbytes)
1250{
1251 char *p;
1252 size_t aligned;
1253
1254 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001255 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001256 size_t len;
1257 size_t blocksize;
1258 struct stack_block *sp;
1259
1260 blocksize = aligned;
1261 if (blocksize < MINSIZE)
1262 blocksize = MINSIZE;
1263 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1264 if (len < blocksize)
1265 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1266 INT_OFF;
1267 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001268 sp->prev = g_stackp;
1269 g_stacknxt = sp->space;
1270 g_stacknleft = blocksize;
1271 sstrend = g_stacknxt + blocksize;
1272 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001273 INT_ON;
1274 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001275 p = g_stacknxt;
1276 g_stacknxt += aligned;
1277 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001278 return p;
1279}
1280
Denis Vlasenko597906c2008-02-20 16:38:54 +00001281static void *
1282stzalloc(size_t nbytes)
1283{
1284 return memset(stalloc(nbytes), 0, nbytes);
1285}
1286
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001287static void
1288stunalloc(void *p)
1289{
1290#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001291 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001292 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001293 abort();
1294 }
1295#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001296 g_stacknleft += g_stacknxt - (char *)p;
1297 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001298}
1299
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001300/*
1301 * Like strdup but works with the ash stack.
1302 */
1303static char *
1304ststrdup(const char *p)
1305{
1306 size_t len = strlen(p) + 1;
1307 return memcpy(stalloc(len), p, len);
1308}
1309
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001310static void
1311setstackmark(struct stackmark *mark)
1312{
Denis Vlasenko01631112007-12-16 17:20:38 +00001313 mark->stackp = g_stackp;
1314 mark->stacknxt = g_stacknxt;
1315 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001316 mark->marknext = markp;
1317 markp = mark;
1318}
1319
1320static void
1321popstackmark(struct stackmark *mark)
1322{
1323 struct stack_block *sp;
1324
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001325 if (!mark->stackp)
1326 return;
1327
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001328 INT_OFF;
1329 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001330 while (g_stackp != mark->stackp) {
1331 sp = g_stackp;
1332 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001333 free(sp);
1334 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001335 g_stacknxt = mark->stacknxt;
1336 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001337 sstrend = mark->stacknxt + mark->stacknleft;
1338 INT_ON;
1339}
1340
1341/*
1342 * When the parser reads in a string, it wants to stick the string on the
1343 * stack and only adjust the stack pointer when it knows how big the
1344 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1345 * of space on top of the stack and stackblocklen returns the length of
1346 * this block. Growstackblock will grow this space by at least one byte,
1347 * possibly moving it (like realloc). Grabstackblock actually allocates the
1348 * part of the block that has been used.
1349 */
1350static void
1351growstackblock(void)
1352{
1353 size_t newlen;
1354
Denis Vlasenko01631112007-12-16 17:20:38 +00001355 newlen = g_stacknleft * 2;
1356 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001357 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1358 if (newlen < 128)
1359 newlen += 128;
1360
Denis Vlasenko01631112007-12-16 17:20:38 +00001361 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001362 struct stack_block *oldstackp;
1363 struct stackmark *xmark;
1364 struct stack_block *sp;
1365 struct stack_block *prevstackp;
1366 size_t grosslen;
1367
1368 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001369 oldstackp = g_stackp;
1370 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001371 prevstackp = sp->prev;
1372 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1373 sp = ckrealloc(sp, grosslen);
1374 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001375 g_stackp = sp;
1376 g_stacknxt = sp->space;
1377 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001378 sstrend = sp->space + newlen;
1379
1380 /*
1381 * Stack marks pointing to the start of the old block
1382 * must be relocated to point to the new block
1383 */
1384 xmark = markp;
1385 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001386 xmark->stackp = g_stackp;
1387 xmark->stacknxt = g_stacknxt;
1388 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001389 xmark = xmark->marknext;
1390 }
1391 INT_ON;
1392 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001393 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001394 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001395 char *p = stalloc(newlen);
1396
1397 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001398 g_stacknxt = memcpy(p, oldspace, oldlen);
1399 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001400 }
1401}
1402
1403static void
1404grabstackblock(size_t len)
1405{
1406 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001407 g_stacknxt += len;
1408 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001409}
1410
1411/*
1412 * The following routines are somewhat easier to use than the above.
1413 * The user declares a variable of type STACKSTR, which may be declared
1414 * to be a register. The macro STARTSTACKSTR initializes things. Then
1415 * the user uses the macro STPUTC to add characters to the string. In
1416 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1417 * grown as necessary. When the user is done, she can just leave the
1418 * string there and refer to it using stackblock(). Or she can allocate
1419 * the space for it using grabstackstr(). If it is necessary to allow
1420 * someone else to use the stack temporarily and then continue to grow
1421 * the string, the user should use grabstack to allocate the space, and
1422 * then call ungrabstr(p) to return to the previous mode of operation.
1423 *
1424 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1425 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1426 * is space for at least one character.
1427 */
1428static void *
1429growstackstr(void)
1430{
1431 size_t len = stackblocksize();
1432 if (herefd >= 0 && len >= 1024) {
1433 full_write(herefd, stackblock(), len);
1434 return stackblock();
1435 }
1436 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001437 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001438}
1439
1440/*
1441 * Called from CHECKSTRSPACE.
1442 */
1443static char *
1444makestrspace(size_t newlen, char *p)
1445{
Denis Vlasenko01631112007-12-16 17:20:38 +00001446 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001447 size_t size = stackblocksize();
1448
1449 for (;;) {
1450 size_t nleft;
1451
1452 size = stackblocksize();
1453 nleft = size - len;
1454 if (nleft >= newlen)
1455 break;
1456 growstackblock();
1457 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001458 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001459}
1460
1461static char *
1462stack_nputstr(const char *s, size_t n, char *p)
1463{
1464 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001465 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001466 return p;
1467}
1468
1469static char *
1470stack_putstr(const char *s, char *p)
1471{
1472 return stack_nputstr(s, strlen(s), p);
1473}
1474
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001475static char *
1476_STPUTC(int c, char *p)
1477{
1478 if (p == sstrend)
1479 p = growstackstr();
1480 *p++ = c;
1481 return p;
1482}
1483
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001484#define STARTSTACKSTR(p) ((p) = stackblock())
1485#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001486#define CHECKSTRSPACE(n, p) do { \
1487 char *q = (p); \
1488 size_t l = (n); \
1489 size_t m = sstrend - q; \
1490 if (l > m) \
1491 (p) = makestrspace(l, q); \
1492} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001493#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001494#define STACKSTRNUL(p) do { \
1495 if ((p) == sstrend) \
1496 (p) = growstackstr(); \
1497 *(p) = '\0'; \
1498} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001499#define STUNPUTC(p) (--(p))
1500#define STTOPC(p) ((p)[-1])
1501#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001502
1503#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001504#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001505#define stackstrend() ((void *)sstrend)
1506
1507
1508/* ============ String helpers */
1509
1510/*
1511 * prefix -- see if pfx is a prefix of string.
1512 */
1513static char *
1514prefix(const char *string, const char *pfx)
1515{
1516 while (*pfx) {
1517 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001518 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001519 }
1520 return (char *) string;
1521}
1522
1523/*
1524 * Check for a valid number. This should be elsewhere.
1525 */
1526static int
1527is_number(const char *p)
1528{
1529 do {
1530 if (!isdigit(*p))
1531 return 0;
1532 } while (*++p != '\0');
1533 return 1;
1534}
1535
1536/*
1537 * Convert a string of digits to an integer, printing an error message on
1538 * failure.
1539 */
1540static int
1541number(const char *s)
1542{
1543 if (!is_number(s))
1544 ash_msg_and_raise_error(illnum, s);
1545 return atoi(s);
1546}
1547
1548/*
1549 * Produce a possibly single quoted string suitable as input to the shell.
1550 * The return string is allocated on the stack.
1551 */
1552static char *
1553single_quote(const char *s)
1554{
1555 char *p;
1556
1557 STARTSTACKSTR(p);
1558
1559 do {
1560 char *q;
1561 size_t len;
1562
1563 len = strchrnul(s, '\'') - s;
1564
1565 q = p = makestrspace(len + 3, p);
1566
1567 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001568 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001569 *q++ = '\'';
1570 s += len;
1571
1572 STADJUST(q - p, p);
1573
1574 len = strspn(s, "'");
1575 if (!len)
1576 break;
1577
1578 q = p = makestrspace(len + 3, p);
1579
1580 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001581 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001582 *q++ = '"';
1583 s += len;
1584
1585 STADJUST(q - p, p);
1586 } while (*s);
1587
1588 USTPUTC(0, p);
1589
1590 return stackblock();
1591}
1592
1593
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001594/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001595
1596static char **argptr; /* argument list for builtin commands */
1597static char *optionarg; /* set by nextopt (like getopt) */
1598static char *optptr; /* used by nextopt */
1599
1600/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001601 * XXX - should get rid of. Have all builtins use getopt(3).
1602 * The library getopt must have the BSD extension static variable
1603 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001604 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001605 * Standard option processing (a la getopt) for builtin routines.
1606 * The only argument that is passed to nextopt is the option string;
1607 * the other arguments are unnecessary. It returns the character,
1608 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001609 */
1610static int
1611nextopt(const char *optstring)
1612{
1613 char *p;
1614 const char *q;
1615 char c;
1616
1617 p = optptr;
1618 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001619 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001620 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001621 if (p == NULL)
1622 return '\0';
1623 if (*p != '-')
1624 return '\0';
1625 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001626 return '\0';
1627 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001628 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001629 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001630 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001631 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001632 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001633 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001634 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001635 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001636 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001637 if (*++q == ':')
1638 q++;
1639 }
1640 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001641 if (*p == '\0') {
1642 p = *argptr++;
1643 if (p == NULL)
1644 ash_msg_and_raise_error("no arg for -%c option", c);
1645 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001646 optionarg = p;
1647 p = NULL;
1648 }
1649 optptr = p;
1650 return c;
1651}
1652
1653
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001654/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001655
Denis Vlasenko01631112007-12-16 17:20:38 +00001656/*
1657 * The parsefile structure pointed to by the global variable parsefile
1658 * contains information about the current file being read.
1659 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001660struct shparam {
1661 int nparam; /* # of positional parameters (without $0) */
1662#if ENABLE_ASH_GETOPTS
1663 int optind; /* next parameter to be processed by getopts */
1664 int optoff; /* used by getopts */
1665#endif
1666 unsigned char malloced; /* if parameter list dynamically allocated */
1667 char **p; /* parameter list */
1668};
1669
1670/*
1671 * Free the list of positional parameters.
1672 */
1673static void
1674freeparam(volatile struct shparam *param)
1675{
Denis Vlasenko01631112007-12-16 17:20:38 +00001676 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001677 char **ap, **ap1;
1678 ap = ap1 = param->p;
1679 while (*ap)
1680 free(*ap++);
1681 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001682 }
1683}
1684
1685#if ENABLE_ASH_GETOPTS
1686static void getoptsreset(const char *value);
1687#endif
1688
1689struct var {
1690 struct var *next; /* next entry in hash list */
1691 int flags; /* flags are defined above */
1692 const char *text; /* name=value */
1693 void (*func)(const char *); /* function to be called when */
1694 /* the variable gets set/unset */
1695};
1696
1697struct localvar {
1698 struct localvar *next; /* next local variable in list */
1699 struct var *vp; /* the variable that was made local */
1700 int flags; /* saved flags */
1701 const char *text; /* saved text */
1702};
1703
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001704/* flags */
1705#define VEXPORT 0x01 /* variable is exported */
1706#define VREADONLY 0x02 /* variable cannot be modified */
1707#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1708#define VTEXTFIXED 0x08 /* text is statically allocated */
1709#define VSTACK 0x10 /* text is allocated on the stack */
1710#define VUNSET 0x20 /* the variable is not set */
1711#define VNOFUNC 0x40 /* don't call the callback function */
1712#define VNOSET 0x80 /* do not set variable - just readonly test */
1713#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001714#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001715# define VDYNAMIC 0x200 /* dynamic variable */
1716#else
1717# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001718#endif
1719
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001720#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001721static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001722#define defifs (defifsvar + 4)
1723#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001724static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001725#endif
1726
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001727
Denis Vlasenko01631112007-12-16 17:20:38 +00001728/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001729#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001730static void
1731change_lc_all(const char *value)
1732{
1733 if (value && *value != '\0')
1734 setlocale(LC_ALL, value);
1735}
1736static void
1737change_lc_ctype(const char *value)
1738{
1739 if (value && *value != '\0')
1740 setlocale(LC_CTYPE, value);
1741}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001742#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743#if ENABLE_ASH_MAIL
1744static void chkmail(void);
1745static void changemail(const char *);
1746#endif
1747static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#if ENABLE_ASH_RANDOM_SUPPORT
1749static void change_random(const char *);
1750#endif
1751
Denis Vlasenko01631112007-12-16 17:20:38 +00001752static const struct {
1753 int flags;
1754 const char *text;
1755 void (*func)(const char *);
1756} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001758 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001760 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001761#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001763 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1764 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001766 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1767 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1768 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1769 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001770#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001771 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#endif
1773#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001774 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001775#endif
1776#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001777 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1778 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001779#endif
1780#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001781 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001782#endif
1783};
1784
Denis Vlasenko0b769642008-07-24 07:54:57 +00001785struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001786
1787struct globals_var {
1788 struct shparam shellparam; /* $@ current positional parameters */
1789 struct redirtab *redirlist;
1790 int g_nullredirs;
1791 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1792 struct var *vartab[VTABSIZE];
1793 struct var varinit[ARRAY_SIZE(varinit_data)];
1794};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001795extern struct globals_var *const ash_ptr_to_globals_var;
1796#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001797#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001798//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001799#define g_nullredirs (G_var.g_nullredirs )
1800#define preverrout_fd (G_var.preverrout_fd)
1801#define vartab (G_var.vartab )
1802#define varinit (G_var.varinit )
1803#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001804 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001805 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1806 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001807 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1808 varinit[i].flags = varinit_data[i].flags; \
1809 varinit[i].text = varinit_data[i].text; \
1810 varinit[i].func = varinit_data[i].func; \
1811 } \
1812} while (0)
1813
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001814#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001815#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001816# define vmail (&vifs)[1]
1817# define vmpath (&vmail)[1]
1818# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001819#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001820# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001821#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001822#define vps1 (&vpath)[1]
1823#define vps2 (&vps1)[1]
1824#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001825#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001826# define voptind (&vps4)[1]
1827# if ENABLE_ASH_RANDOM_SUPPORT
1828# define vrandom (&voptind)[1]
1829# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001831# if ENABLE_ASH_RANDOM_SUPPORT
1832# define vrandom (&vps4)[1]
1833# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001834#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001835
1836/*
1837 * The following macros access the values of the above variables.
1838 * They have to skip over the name. They return the null string
1839 * for unset variables.
1840 */
1841#define ifsval() (vifs.text + 4)
1842#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001843#if ENABLE_ASH_MAIL
1844# define mailval() (vmail.text + 5)
1845# define mpathval() (vmpath.text + 9)
1846# define mpathset() ((vmpath.flags & VUNSET) == 0)
1847#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001848#define pathval() (vpath.text + 5)
1849#define ps1val() (vps1.text + 4)
1850#define ps2val() (vps2.text + 4)
1851#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001852#if ENABLE_ASH_GETOPTS
1853# define optindval() (voptind.text + 7)
1854#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001855
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001856
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001857#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1858#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1859
Denis Vlasenko01631112007-12-16 17:20:38 +00001860#if ENABLE_ASH_GETOPTS
1861static void
1862getoptsreset(const char *value)
1863{
1864 shellparam.optind = number(value);
1865 shellparam.optoff = -1;
1866}
1867#endif
1868
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001869/*
1870 * Return of a legal variable name (a letter or underscore followed by zero or
1871 * more letters, underscores, and digits).
1872 */
1873static char *
1874endofname(const char *name)
1875{
1876 char *p;
1877
1878 p = (char *) name;
1879 if (!is_name(*p))
1880 return p;
1881 while (*++p) {
1882 if (!is_in_name(*p))
1883 break;
1884 }
1885 return p;
1886}
1887
1888/*
1889 * Compares two strings up to the first = or '\0'. The first
1890 * string must be terminated by '='; the second may be terminated by
1891 * either '=' or '\0'.
1892 */
1893static int
1894varcmp(const char *p, const char *q)
1895{
1896 int c, d;
1897
1898 while ((c = *p) == (d = *q)) {
1899 if (!c || c == '=')
1900 goto out;
1901 p++;
1902 q++;
1903 }
1904 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001905 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001906 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001907 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001908 out:
1909 return c - d;
1910}
1911
1912static int
1913varequal(const char *a, const char *b)
1914{
1915 return !varcmp(a, b);
1916}
1917
1918/*
1919 * Find the appropriate entry in the hash table from the name.
1920 */
1921static struct var **
1922hashvar(const char *p)
1923{
1924 unsigned hashval;
1925
1926 hashval = ((unsigned char) *p) << 4;
1927 while (*p && *p != '=')
1928 hashval += (unsigned char) *p++;
1929 return &vartab[hashval % VTABSIZE];
1930}
1931
1932static int
1933vpcmp(const void *a, const void *b)
1934{
1935 return varcmp(*(const char **)a, *(const char **)b);
1936}
1937
1938/*
1939 * This routine initializes the builtin variables.
1940 */
1941static void
1942initvar(void)
1943{
1944 struct var *vp;
1945 struct var *end;
1946 struct var **vpp;
1947
1948 /*
1949 * PS1 depends on uid
1950 */
1951#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1952 vps1.text = "PS1=\\w \\$ ";
1953#else
1954 if (!geteuid())
1955 vps1.text = "PS1=# ";
1956#endif
1957 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001958 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001959 do {
1960 vpp = hashvar(vp->text);
1961 vp->next = *vpp;
1962 *vpp = vp;
1963 } while (++vp < end);
1964}
1965
1966static struct var **
1967findvar(struct var **vpp, const char *name)
1968{
1969 for (; *vpp; vpp = &(*vpp)->next) {
1970 if (varequal((*vpp)->text, name)) {
1971 break;
1972 }
1973 }
1974 return vpp;
1975}
1976
1977/*
1978 * Find the value of a variable. Returns NULL if not set.
1979 */
1980static char *
1981lookupvar(const char *name)
1982{
1983 struct var *v;
1984
1985 v = *findvar(hashvar(name), name);
1986 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001987#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001988 /*
1989 * Dynamic variables are implemented roughly the same way they are
1990 * in bash. Namely, they're "special" so long as they aren't unset.
1991 * As soon as they're unset, they're no longer dynamic, and dynamic
1992 * lookup will no longer happen at that point. -- PFM.
1993 */
1994 if ((v->flags & VDYNAMIC))
1995 (*v->func)(NULL);
1996#endif
1997 if (!(v->flags & VUNSET))
1998 return strchrnul(v->text, '=') + 1;
1999 }
2000 return NULL;
2001}
2002
2003/*
2004 * Search the environment of a builtin command.
2005 */
2006static char *
2007bltinlookup(const char *name)
2008{
2009 struct strlist *sp;
2010
2011 for (sp = cmdenviron; sp; sp = sp->next) {
2012 if (varequal(sp->text, name))
2013 return strchrnul(sp->text, '=') + 1;
2014 }
2015 return lookupvar(name);
2016}
2017
2018/*
2019 * Same as setvar except that the variable and value are passed in
2020 * the first argument as name=value. Since the first argument will
2021 * be actually stored in the table, it should not be a string that
2022 * will go away.
2023 * Called with interrupts off.
2024 */
2025static void
2026setvareq(char *s, int flags)
2027{
2028 struct var *vp, **vpp;
2029
2030 vpp = hashvar(s);
2031 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2032 vp = *findvar(vpp, s);
2033 if (vp) {
2034 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2035 const char *n;
2036
2037 if (flags & VNOSAVE)
2038 free(s);
2039 n = vp->text;
2040 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2041 }
2042
2043 if (flags & VNOSET)
2044 return;
2045
2046 if (vp->func && (flags & VNOFUNC) == 0)
2047 (*vp->func)(strchrnul(s, '=') + 1);
2048
2049 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2050 free((char*)vp->text);
2051
2052 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2053 } else {
2054 if (flags & VNOSET)
2055 return;
2056 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002057 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002058 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002059 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002060 *vpp = vp;
2061 }
2062 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2063 s = ckstrdup(s);
2064 vp->text = s;
2065 vp->flags = flags;
2066}
2067
2068/*
2069 * Set the value of a variable. The flags argument is ored with the
2070 * flags of the variable. If val is NULL, the variable is unset.
2071 */
2072static void
2073setvar(const char *name, const char *val, int flags)
2074{
2075 char *p, *q;
2076 size_t namelen;
2077 char *nameeq;
2078 size_t vallen;
2079
2080 q = endofname(name);
2081 p = strchrnul(q, '=');
2082 namelen = p - name;
2083 if (!namelen || p != q)
2084 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2085 vallen = 0;
2086 if (val == NULL) {
2087 flags |= VUNSET;
2088 } else {
2089 vallen = strlen(val);
2090 }
2091 INT_OFF;
2092 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002093 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094 if (val) {
2095 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002096 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002097 }
2098 *p = '\0';
2099 setvareq(nameeq, flags | VNOSAVE);
2100 INT_ON;
2101}
2102
2103#if ENABLE_ASH_GETOPTS
2104/*
2105 * Safe version of setvar, returns 1 on success 0 on failure.
2106 */
2107static int
2108setvarsafe(const char *name, const char *val, int flags)
2109{
2110 int err;
2111 volatile int saveint;
2112 struct jmploc *volatile savehandler = exception_handler;
2113 struct jmploc jmploc;
2114
2115 SAVE_INT(saveint);
2116 if (setjmp(jmploc.loc))
2117 err = 1;
2118 else {
2119 exception_handler = &jmploc;
2120 setvar(name, val, flags);
2121 err = 0;
2122 }
2123 exception_handler = savehandler;
2124 RESTORE_INT(saveint);
2125 return err;
2126}
2127#endif
2128
2129/*
2130 * Unset the specified variable.
2131 */
2132static int
2133unsetvar(const char *s)
2134{
2135 struct var **vpp;
2136 struct var *vp;
2137 int retval;
2138
2139 vpp = findvar(hashvar(s), s);
2140 vp = *vpp;
2141 retval = 2;
2142 if (vp) {
2143 int flags = vp->flags;
2144
2145 retval = 1;
2146 if (flags & VREADONLY)
2147 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002148#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002149 vp->flags &= ~VDYNAMIC;
2150#endif
2151 if (flags & VUNSET)
2152 goto ok;
2153 if ((flags & VSTRFIXED) == 0) {
2154 INT_OFF;
2155 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2156 free((char*)vp->text);
2157 *vpp = vp->next;
2158 free(vp);
2159 INT_ON;
2160 } else {
2161 setvar(s, 0, 0);
2162 vp->flags &= ~VEXPORT;
2163 }
2164 ok:
2165 retval = 0;
2166 }
2167 out:
2168 return retval;
2169}
2170
2171/*
2172 * Process a linked list of variable assignments.
2173 */
2174static void
2175listsetvar(struct strlist *list_set_var, int flags)
2176{
2177 struct strlist *lp = list_set_var;
2178
2179 if (!lp)
2180 return;
2181 INT_OFF;
2182 do {
2183 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002184 lp = lp->next;
2185 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186 INT_ON;
2187}
2188
2189/*
2190 * Generate a list of variables satisfying the given conditions.
2191 */
2192static char **
2193listvars(int on, int off, char ***end)
2194{
2195 struct var **vpp;
2196 struct var *vp;
2197 char **ep;
2198 int mask;
2199
2200 STARTSTACKSTR(ep);
2201 vpp = vartab;
2202 mask = on | off;
2203 do {
2204 for (vp = *vpp; vp; vp = vp->next) {
2205 if ((vp->flags & mask) == on) {
2206 if (ep == stackstrend())
2207 ep = growstackstr();
2208 *ep++ = (char *) vp->text;
2209 }
2210 }
2211 } while (++vpp < vartab + VTABSIZE);
2212 if (ep == stackstrend())
2213 ep = growstackstr();
2214 if (end)
2215 *end = ep;
2216 *ep++ = NULL;
2217 return grabstackstr(ep);
2218}
2219
2220
2221/* ============ Path search helper
2222 *
2223 * The variable path (passed by reference) should be set to the start
2224 * of the path before the first call; padvance will update
2225 * this value as it proceeds. Successive calls to padvance will return
2226 * the possible path expansions in sequence. If an option (indicated by
2227 * a percent sign) appears in the path entry then the global variable
2228 * pathopt will be set to point to it; otherwise pathopt will be set to
2229 * NULL.
2230 */
2231static const char *pathopt; /* set by padvance */
2232
2233static char *
2234padvance(const char **path, const char *name)
2235{
2236 const char *p;
2237 char *q;
2238 const char *start;
2239 size_t len;
2240
2241 if (*path == NULL)
2242 return NULL;
2243 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002244 for (p = start; *p && *p != ':' && *p != '%'; p++)
2245 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002246 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2247 while (stackblocksize() < len)
2248 growstackblock();
2249 q = stackblock();
2250 if (p != start) {
2251 memcpy(q, start, p - start);
2252 q += p - start;
2253 *q++ = '/';
2254 }
2255 strcpy(q, name);
2256 pathopt = NULL;
2257 if (*p == '%') {
2258 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002259 while (*p && *p != ':')
2260 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002261 }
2262 if (*p == ':')
2263 *path = p + 1;
2264 else
2265 *path = NULL;
2266 return stalloc(len);
2267}
2268
2269
2270/* ============ Prompt */
2271
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002272static smallint doprompt; /* if set, prompt the user */
2273static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002274
2275#if ENABLE_FEATURE_EDITING
2276static line_input_t *line_input_state;
2277static const char *cmdedit_prompt;
2278static void
2279putprompt(const char *s)
2280{
2281 if (ENABLE_ASH_EXPAND_PRMT) {
2282 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002283 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002284 return;
2285 }
2286 cmdedit_prompt = s;
2287}
2288#else
2289static void
2290putprompt(const char *s)
2291{
2292 out2str(s);
2293}
2294#endif
2295
2296#if ENABLE_ASH_EXPAND_PRMT
2297/* expandstr() needs parsing machinery, so it is far away ahead... */
2298static const char *expandstr(const char *ps);
2299#else
2300#define expandstr(s) s
2301#endif
2302
2303static void
2304setprompt(int whichprompt)
2305{
2306 const char *prompt;
2307#if ENABLE_ASH_EXPAND_PRMT
2308 struct stackmark smark;
2309#endif
2310
2311 needprompt = 0;
2312
2313 switch (whichprompt) {
2314 case 1:
2315 prompt = ps1val();
2316 break;
2317 case 2:
2318 prompt = ps2val();
2319 break;
2320 default: /* 0 */
2321 prompt = nullstr;
2322 }
2323#if ENABLE_ASH_EXPAND_PRMT
2324 setstackmark(&smark);
2325 stalloc(stackblocksize());
2326#endif
2327 putprompt(expandstr(prompt));
2328#if ENABLE_ASH_EXPAND_PRMT
2329 popstackmark(&smark);
2330#endif
2331}
2332
2333
2334/* ============ The cd and pwd commands */
2335
2336#define CD_PHYSICAL 1
2337#define CD_PRINT 2
2338
2339static int docd(const char *, int);
2340
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002341static int
2342cdopt(void)
2343{
2344 int flags = 0;
2345 int i, j;
2346
2347 j = 'L';
2348 while ((i = nextopt("LP"))) {
2349 if (i != j) {
2350 flags ^= CD_PHYSICAL;
2351 j = i;
2352 }
2353 }
2354
2355 return flags;
2356}
2357
2358/*
2359 * Update curdir (the name of the current directory) in response to a
2360 * cd command.
2361 */
2362static const char *
2363updatepwd(const char *dir)
2364{
2365 char *new;
2366 char *p;
2367 char *cdcomppath;
2368 const char *lim;
2369
2370 cdcomppath = ststrdup(dir);
2371 STARTSTACKSTR(new);
2372 if (*dir != '/') {
2373 if (curdir == nullstr)
2374 return 0;
2375 new = stack_putstr(curdir, new);
2376 }
2377 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002378 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002379 if (*dir != '/') {
2380 if (new[-1] != '/')
2381 USTPUTC('/', new);
2382 if (new > lim && *lim == '/')
2383 lim++;
2384 } else {
2385 USTPUTC('/', new);
2386 cdcomppath++;
2387 if (dir[1] == '/' && dir[2] != '/') {
2388 USTPUTC('/', new);
2389 cdcomppath++;
2390 lim++;
2391 }
2392 }
2393 p = strtok(cdcomppath, "/");
2394 while (p) {
2395 switch (*p) {
2396 case '.':
2397 if (p[1] == '.' && p[2] == '\0') {
2398 while (new > lim) {
2399 STUNPUTC(new);
2400 if (new[-1] == '/')
2401 break;
2402 }
2403 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002404 }
2405 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002406 break;
2407 /* fall through */
2408 default:
2409 new = stack_putstr(p, new);
2410 USTPUTC('/', new);
2411 }
2412 p = strtok(0, "/");
2413 }
2414 if (new > lim)
2415 STUNPUTC(new);
2416 *new = 0;
2417 return stackblock();
2418}
2419
2420/*
2421 * Find out what the current directory is. If we already know the current
2422 * directory, this routine returns immediately.
2423 */
2424static char *
2425getpwd(void)
2426{
Denis Vlasenko01631112007-12-16 17:20:38 +00002427 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002428 return dir ? dir : nullstr;
2429}
2430
2431static void
2432setpwd(const char *val, int setold)
2433{
2434 char *oldcur, *dir;
2435
2436 oldcur = dir = curdir;
2437
2438 if (setold) {
2439 setvar("OLDPWD", oldcur, VEXPORT);
2440 }
2441 INT_OFF;
2442 if (physdir != nullstr) {
2443 if (physdir != oldcur)
2444 free(physdir);
2445 physdir = nullstr;
2446 }
2447 if (oldcur == val || !val) {
2448 char *s = getpwd();
2449 physdir = s;
2450 if (!val)
2451 dir = s;
2452 } else
2453 dir = ckstrdup(val);
2454 if (oldcur != dir && oldcur != nullstr) {
2455 free(oldcur);
2456 }
2457 curdir = dir;
2458 INT_ON;
2459 setvar("PWD", dir, VEXPORT);
2460}
2461
2462static void hashcd(void);
2463
2464/*
2465 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2466 * know that the current directory has changed.
2467 */
2468static int
2469docd(const char *dest, int flags)
2470{
2471 const char *dir = 0;
2472 int err;
2473
2474 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2475
2476 INT_OFF;
2477 if (!(flags & CD_PHYSICAL)) {
2478 dir = updatepwd(dest);
2479 if (dir)
2480 dest = dir;
2481 }
2482 err = chdir(dest);
2483 if (err)
2484 goto out;
2485 setpwd(dir, 1);
2486 hashcd();
2487 out:
2488 INT_ON;
2489 return err;
2490}
2491
2492static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002493cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002494{
2495 const char *dest;
2496 const char *path;
2497 const char *p;
2498 char c;
2499 struct stat statb;
2500 int flags;
2501
2502 flags = cdopt();
2503 dest = *argptr;
2504 if (!dest)
2505 dest = bltinlookup(homestr);
2506 else if (LONE_DASH(dest)) {
2507 dest = bltinlookup("OLDPWD");
2508 flags |= CD_PRINT;
2509 }
2510 if (!dest)
2511 dest = nullstr;
2512 if (*dest == '/')
2513 goto step7;
2514 if (*dest == '.') {
2515 c = dest[1];
2516 dotdot:
2517 switch (c) {
2518 case '\0':
2519 case '/':
2520 goto step6;
2521 case '.':
2522 c = dest[2];
2523 if (c != '.')
2524 goto dotdot;
2525 }
2526 }
2527 if (!*dest)
2528 dest = ".";
2529 path = bltinlookup("CDPATH");
2530 if (!path) {
2531 step6:
2532 step7:
2533 p = dest;
2534 goto docd;
2535 }
2536 do {
2537 c = *path;
2538 p = padvance(&path, dest);
2539 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2540 if (c && c != ':')
2541 flags |= CD_PRINT;
2542 docd:
2543 if (!docd(p, flags))
2544 goto out;
2545 break;
2546 }
2547 } while (path);
2548 ash_msg_and_raise_error("can't cd to %s", dest);
2549 /* NOTREACHED */
2550 out:
2551 if (flags & CD_PRINT)
2552 out1fmt(snlfmt, curdir);
2553 return 0;
2554}
2555
2556static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002557pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002558{
2559 int flags;
2560 const char *dir = curdir;
2561
2562 flags = cdopt();
2563 if (flags) {
2564 if (physdir == nullstr)
2565 setpwd(dir, 0);
2566 dir = physdir;
2567 }
2568 out1fmt(snlfmt, dir);
2569 return 0;
2570}
2571
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002572
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002573/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002574
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002575#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002576#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002577
Eric Andersenc470f442003-07-28 09:56:35 +00002578/* Syntax classes */
2579#define CWORD 0 /* character is nothing special */
2580#define CNL 1 /* newline character */
2581#define CBACK 2 /* a backslash character */
2582#define CSQUOTE 3 /* single quote */
2583#define CDQUOTE 4 /* double quote */
2584#define CENDQUOTE 5 /* a terminating quote */
2585#define CBQUOTE 6 /* backwards single quote */
2586#define CVAR 7 /* a dollar sign */
2587#define CENDVAR 8 /* a '}' character */
2588#define CLP 9 /* a left paren in arithmetic */
2589#define CRP 10 /* a right paren in arithmetic */
2590#define CENDFILE 11 /* end of file */
2591#define CCTL 12 /* like CWORD, except it must be escaped */
2592#define CSPCL 13 /* these terminate a word */
2593#define CIGN 14 /* character should be ignored */
2594
Denis Vlasenko131ae172007-02-18 13:00:19 +00002595#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002596#define SYNBASE 130
2597#define PEOF -130
2598#define PEOA -129
2599#define PEOA_OR_PEOF PEOA
2600#else
2601#define SYNBASE 129
2602#define PEOF -129
2603#define PEOA_OR_PEOF PEOF
2604#endif
2605
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002606/* number syntax index */
2607#define BASESYNTAX 0 /* not in quotes */
2608#define DQSYNTAX 1 /* in double quotes */
2609#define SQSYNTAX 2 /* in single quotes */
2610#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002611#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002612
Denis Vlasenko131ae172007-02-18 13:00:19 +00002613#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002614#define USE_SIT_FUNCTION
2615#endif
2616
Denis Vlasenko131ae172007-02-18 13:00:19 +00002617#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002618static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002619#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002620 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002621#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002622 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2623 { CNL, CNL, CNL, CNL }, /* 2, \n */
2624 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2625 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2626 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2627 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2628 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2629 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2630 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2631 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2632 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002633#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002634 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2635 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2636 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002637#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002638};
Eric Andersenc470f442003-07-28 09:56:35 +00002639#else
2640static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002641#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002642 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002643#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002644 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2645 { CNL, CNL, CNL }, /* 2, \n */
2646 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2647 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2648 { CVAR, CVAR, CWORD }, /* 5, $ */
2649 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2650 { CSPCL, CWORD, CWORD }, /* 7, ( */
2651 { CSPCL, CWORD, CWORD }, /* 8, ) */
2652 { CBACK, CBACK, CCTL }, /* 9, \ */
2653 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2654 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002655#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002656 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2657 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2658 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002659#endif
2660};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002661#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002662
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002663#ifdef USE_SIT_FUNCTION
2664
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002665static int
2666SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002667{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002668 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002669#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002670 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002671 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2672 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2673 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2674 11, 3 /* "}~" */
2675 };
2676#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002677 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002678 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2679 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2680 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2681 10, 2 /* "}~" */
2682 };
2683#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002684 const char *s;
2685 int indx;
2686
Eric Andersenc470f442003-07-28 09:56:35 +00002687 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002688 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002689#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002690 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002691 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002692 else
2693#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002694
2695 if ((unsigned char)c >= (unsigned char)(CTLESC)
2696 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2697 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002698 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002699 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002700 s = strchrnul(spec_symbls, c);
2701 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002702 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002703 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002704 }
2705 return S_I_T[indx][syntax];
2706}
2707
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002708#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002709
Denis Vlasenko131ae172007-02-18 13:00:19 +00002710#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002711#define CSPCL_CIGN_CIGN_CIGN 0
2712#define CSPCL_CWORD_CWORD_CWORD 1
2713#define CNL_CNL_CNL_CNL 2
2714#define CWORD_CCTL_CCTL_CWORD 3
2715#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2716#define CVAR_CVAR_CWORD_CVAR 5
2717#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2718#define CSPCL_CWORD_CWORD_CLP 7
2719#define CSPCL_CWORD_CWORD_CRP 8
2720#define CBACK_CBACK_CCTL_CBACK 9
2721#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2722#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2723#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2724#define CWORD_CWORD_CWORD_CWORD 13
2725#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002726#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002727#define CSPCL_CWORD_CWORD_CWORD 0
2728#define CNL_CNL_CNL_CNL 1
2729#define CWORD_CCTL_CCTL_CWORD 2
2730#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2731#define CVAR_CVAR_CWORD_CVAR 4
2732#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2733#define CSPCL_CWORD_CWORD_CLP 6
2734#define CSPCL_CWORD_CWORD_CRP 7
2735#define CBACK_CBACK_CCTL_CBACK 8
2736#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2737#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2738#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2739#define CWORD_CWORD_CWORD_CWORD 12
2740#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002741#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002742
2743static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002744 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002745 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002746#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002747 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2748#endif
2749 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2751 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2752 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2753 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2754 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2755 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2756 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2757 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002758 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2887 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2888 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2910 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002911 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002912 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2914 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002916 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002917 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2918 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2919 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2920 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2922 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2923 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2924 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2925 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2937 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2938 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2939 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2940 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2969 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2970 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2971 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2974 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3002 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3003 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3004 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003005};
3006
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003007#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3008
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003009#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003010
Eric Andersen2870d962001-07-02 17:27:21 +00003011
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003012/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003013
Denis Vlasenko131ae172007-02-18 13:00:19 +00003014#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003015
3016#define ALIASINUSE 1
3017#define ALIASDEAD 2
3018
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003019struct alias {
3020 struct alias *next;
3021 char *name;
3022 char *val;
3023 int flag;
3024};
3025
Denis Vlasenko01631112007-12-16 17:20:38 +00003026
3027static struct alias **atab; // [ATABSIZE];
3028#define INIT_G_alias() do { \
3029 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3030} while (0)
3031
Eric Andersen2870d962001-07-02 17:27:21 +00003032
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003033static struct alias **
3034__lookupalias(const char *name) {
3035 unsigned int hashval;
3036 struct alias **app;
3037 const char *p;
3038 unsigned int ch;
3039
3040 p = name;
3041
3042 ch = (unsigned char)*p;
3043 hashval = ch << 4;
3044 while (ch) {
3045 hashval += ch;
3046 ch = (unsigned char)*++p;
3047 }
3048 app = &atab[hashval % ATABSIZE];
3049
3050 for (; *app; app = &(*app)->next) {
3051 if (strcmp(name, (*app)->name) == 0) {
3052 break;
3053 }
3054 }
3055
3056 return app;
3057}
3058
3059static struct alias *
3060lookupalias(const char *name, int check)
3061{
3062 struct alias *ap = *__lookupalias(name);
3063
3064 if (check && ap && (ap->flag & ALIASINUSE))
3065 return NULL;
3066 return ap;
3067}
3068
3069static struct alias *
3070freealias(struct alias *ap)
3071{
3072 struct alias *next;
3073
3074 if (ap->flag & ALIASINUSE) {
3075 ap->flag |= ALIASDEAD;
3076 return ap;
3077 }
3078
3079 next = ap->next;
3080 free(ap->name);
3081 free(ap->val);
3082 free(ap);
3083 return next;
3084}
Eric Andersencb57d552001-06-28 07:25:16 +00003085
Eric Andersenc470f442003-07-28 09:56:35 +00003086static void
3087setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003088{
3089 struct alias *ap, **app;
3090
3091 app = __lookupalias(name);
3092 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003093 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003094 if (ap) {
3095 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003096 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003097 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003098 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003099 ap->flag &= ~ALIASDEAD;
3100 } else {
3101 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003102 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003103 ap->name = ckstrdup(name);
3104 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003105 /*ap->flag = 0; - ckzalloc did it */
3106 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003107 *app = ap;
3108 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003109 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003110}
3111
Eric Andersenc470f442003-07-28 09:56:35 +00003112static int
3113unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003114{
Eric Andersencb57d552001-06-28 07:25:16 +00003115 struct alias **app;
3116
3117 app = __lookupalias(name);
3118
3119 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003120 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003121 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003122 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003123 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003124 }
3125
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003126 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003127}
3128
Eric Andersenc470f442003-07-28 09:56:35 +00003129static void
3130rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003131{
Eric Andersencb57d552001-06-28 07:25:16 +00003132 struct alias *ap, **app;
3133 int i;
3134
Denis Vlasenkob012b102007-02-19 22:43:01 +00003135 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003136 for (i = 0; i < ATABSIZE; i++) {
3137 app = &atab[i];
3138 for (ap = *app; ap; ap = *app) {
3139 *app = freealias(*app);
3140 if (ap == *app) {
3141 app = &ap->next;
3142 }
3143 }
3144 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003145 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003146}
3147
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003148static void
3149printalias(const struct alias *ap)
3150{
3151 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3152}
3153
Eric Andersencb57d552001-06-28 07:25:16 +00003154/*
3155 * TODO - sort output
3156 */
Eric Andersenc470f442003-07-28 09:56:35 +00003157static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003158aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003159{
3160 char *n, *v;
3161 int ret = 0;
3162 struct alias *ap;
3163
Denis Vlasenko68404f12008-03-17 09:00:54 +00003164 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003165 int i;
3166
Denis Vlasenko68404f12008-03-17 09:00:54 +00003167 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003168 for (ap = atab[i]; ap; ap = ap->next) {
3169 printalias(ap);
3170 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003171 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003172 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003173 }
3174 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003175 v = strchr(n+1, '=');
3176 if (v == NULL) { /* n+1: funny ksh stuff */
3177 ap = *__lookupalias(n);
3178 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003179 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003180 ret = 1;
3181 } else
3182 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003183 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003184 *v++ = '\0';
3185 setalias(n, v);
3186 }
3187 }
3188
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003189 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003190}
3191
Eric Andersenc470f442003-07-28 09:56:35 +00003192static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003193unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003194{
3195 int i;
3196
3197 while ((i = nextopt("a")) != '\0') {
3198 if (i == 'a') {
3199 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003200 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003201 }
3202 }
3203 for (i = 0; *argptr; argptr++) {
3204 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003205 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003206 i = 1;
3207 }
3208 }
3209
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003210 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003211}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003212
Denis Vlasenko131ae172007-02-18 13:00:19 +00003213#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003214
Eric Andersenc470f442003-07-28 09:56:35 +00003215
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003216/* ============ jobs.c */
3217
3218/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3219#define FORK_FG 0
3220#define FORK_BG 1
3221#define FORK_NOJOB 2
3222
3223/* mode flags for showjob(s) */
3224#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3225#define SHOW_PID 0x04 /* include process pid */
3226#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3227
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003228/*
3229 * A job structure contains information about a job. A job is either a
3230 * single process or a set of processes contained in a pipeline. In the
3231 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3232 * array of pids.
3233 */
3234
3235struct procstat {
3236 pid_t pid; /* process id */
3237 int status; /* last process status from wait() */
3238 char *cmd; /* text of command being run */
3239};
3240
3241struct job {
3242 struct procstat ps0; /* status of process */
3243 struct procstat *ps; /* status or processes when more than one */
3244#if JOBS
3245 int stopstatus; /* status of a stopped job */
3246#endif
3247 uint32_t
3248 nprocs: 16, /* number of processes */
3249 state: 8,
3250#define JOBRUNNING 0 /* at least one proc running */
3251#define JOBSTOPPED 1 /* all procs are stopped */
3252#define JOBDONE 2 /* all procs are completed */
3253#if JOBS
3254 sigint: 1, /* job was killed by SIGINT */
3255 jobctl: 1, /* job running under job control */
3256#endif
3257 waited: 1, /* true if this entry has been waited for */
3258 used: 1, /* true if this entry is in used */
3259 changed: 1; /* true if status has changed */
3260 struct job *prev_job; /* previous job */
3261};
3262
Denis Vlasenko68404f12008-03-17 09:00:54 +00003263static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003264#if !JOBS
3265#define forkshell(job, node, mode) forkshell(job, mode)
3266#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003267static int forkshell(struct job *, union node *, int);
3268static int waitforjob(struct job *);
3269
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003270#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003271enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003272#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003273#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003274static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003275static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003276#endif
3277
3278/*
3279 * Set the signal handler for the specified signal. The routine figures
3280 * out what it should be set to.
3281 */
3282static void
3283setsignal(int signo)
3284{
3285 int action;
3286 char *t, tsig;
3287 struct sigaction act;
3288
3289 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003290 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003291 if (t == NULL)
3292 action = S_DFL;
3293 else if (*t != '\0')
3294 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003295 if (rootshell && action == S_DFL) {
3296 switch (signo) {
3297 case SIGINT:
3298 if (iflag || minusc || sflag == 0)
3299 action = S_CATCH;
3300 break;
3301 case SIGQUIT:
3302#if DEBUG
3303 if (debug)
3304 break;
3305#endif
3306 /* FALLTHROUGH */
3307 case SIGTERM:
3308 if (iflag)
3309 action = S_IGN;
3310 break;
3311#if JOBS
3312 case SIGTSTP:
3313 case SIGTTOU:
3314 if (mflag)
3315 action = S_IGN;
3316 break;
3317#endif
3318 }
3319 }
3320
3321 t = &sigmode[signo - 1];
3322 tsig = *t;
3323 if (tsig == 0) {
3324 /*
3325 * current setting unknown
3326 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003327 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003328 /*
3329 * Pretend it worked; maybe we should give a warning
3330 * here, but other shells don't. We don't alter
3331 * sigmode, so that we retry every time.
3332 */
3333 return;
3334 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003335 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003336 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003337 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338 if (mflag
3339 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3340 ) {
3341 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003342 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003343 }
3344 }
3345 if (tsig == S_HARD_IGN || tsig == action)
3346 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003347 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003348 switch (action) {
3349 case S_CATCH:
3350 act.sa_handler = onsig;
3351 break;
3352 case S_IGN:
3353 act.sa_handler = SIG_IGN;
3354 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003355 }
3356 *t = action;
3357 act.sa_flags = 0;
3358 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003359 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003360}
3361
3362/* mode flags for set_curjob */
3363#define CUR_DELETE 2
3364#define CUR_RUNNING 1
3365#define CUR_STOPPED 0
3366
3367/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003368#define DOWAIT_NONBLOCK WNOHANG
3369#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003370
3371#if JOBS
3372/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003373static int initialpgrp; //references:2
3374static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003375#endif
3376/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003377static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003378/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003379static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003380/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003381static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003382/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003383static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003384
3385static void
3386set_curjob(struct job *jp, unsigned mode)
3387{
3388 struct job *jp1;
3389 struct job **jpp, **curp;
3390
3391 /* first remove from list */
3392 jpp = curp = &curjob;
3393 do {
3394 jp1 = *jpp;
3395 if (jp1 == jp)
3396 break;
3397 jpp = &jp1->prev_job;
3398 } while (1);
3399 *jpp = jp1->prev_job;
3400
3401 /* Then re-insert in correct position */
3402 jpp = curp;
3403 switch (mode) {
3404 default:
3405#if DEBUG
3406 abort();
3407#endif
3408 case CUR_DELETE:
3409 /* job being deleted */
3410 break;
3411 case CUR_RUNNING:
3412 /* newly created job or backgrounded job,
3413 put after all stopped jobs. */
3414 do {
3415 jp1 = *jpp;
3416#if JOBS
3417 if (!jp1 || jp1->state != JOBSTOPPED)
3418#endif
3419 break;
3420 jpp = &jp1->prev_job;
3421 } while (1);
3422 /* FALLTHROUGH */
3423#if JOBS
3424 case CUR_STOPPED:
3425#endif
3426 /* newly stopped job - becomes curjob */
3427 jp->prev_job = *jpp;
3428 *jpp = jp;
3429 break;
3430 }
3431}
3432
3433#if JOBS || DEBUG
3434static int
3435jobno(const struct job *jp)
3436{
3437 return jp - jobtab + 1;
3438}
3439#endif
3440
3441/*
3442 * Convert a job name to a job structure.
3443 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003444#if !JOBS
3445#define getjob(name, getctl) getjob(name)
3446#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003447static struct job *
3448getjob(const char *name, int getctl)
3449{
3450 struct job *jp;
3451 struct job *found;
3452 const char *err_msg = "No such job: %s";
3453 unsigned num;
3454 int c;
3455 const char *p;
3456 char *(*match)(const char *, const char *);
3457
3458 jp = curjob;
3459 p = name;
3460 if (!p)
3461 goto currentjob;
3462
3463 if (*p != '%')
3464 goto err;
3465
3466 c = *++p;
3467 if (!c)
3468 goto currentjob;
3469
3470 if (!p[1]) {
3471 if (c == '+' || c == '%') {
3472 currentjob:
3473 err_msg = "No current job";
3474 goto check;
3475 }
3476 if (c == '-') {
3477 if (jp)
3478 jp = jp->prev_job;
3479 err_msg = "No previous job";
3480 check:
3481 if (!jp)
3482 goto err;
3483 goto gotit;
3484 }
3485 }
3486
3487 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003488// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003489 num = atoi(p);
3490 if (num < njobs) {
3491 jp = jobtab + num - 1;
3492 if (jp->used)
3493 goto gotit;
3494 goto err;
3495 }
3496 }
3497
3498 match = prefix;
3499 if (*p == '?') {
3500 match = strstr;
3501 p++;
3502 }
3503
3504 found = 0;
3505 while (1) {
3506 if (!jp)
3507 goto err;
3508 if (match(jp->ps[0].cmd, p)) {
3509 if (found)
3510 goto err;
3511 found = jp;
3512 err_msg = "%s: ambiguous";
3513 }
3514 jp = jp->prev_job;
3515 }
3516
3517 gotit:
3518#if JOBS
3519 err_msg = "job %s not created under job control";
3520 if (getctl && jp->jobctl == 0)
3521 goto err;
3522#endif
3523 return jp;
3524 err:
3525 ash_msg_and_raise_error(err_msg, name);
3526}
3527
3528/*
3529 * Mark a job structure as unused.
3530 */
3531static void
3532freejob(struct job *jp)
3533{
3534 struct procstat *ps;
3535 int i;
3536
3537 INT_OFF;
3538 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3539 if (ps->cmd != nullstr)
3540 free(ps->cmd);
3541 }
3542 if (jp->ps != &jp->ps0)
3543 free(jp->ps);
3544 jp->used = 0;
3545 set_curjob(jp, CUR_DELETE);
3546 INT_ON;
3547}
3548
3549#if JOBS
3550static void
3551xtcsetpgrp(int fd, pid_t pgrp)
3552{
3553 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003554 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003555}
3556
3557/*
3558 * Turn job control on and off.
3559 *
3560 * Note: This code assumes that the third arg to ioctl is a character
3561 * pointer, which is true on Berkeley systems but not System V. Since
3562 * System V doesn't have job control yet, this isn't a problem now.
3563 *
3564 * Called with interrupts off.
3565 */
3566static void
3567setjobctl(int on)
3568{
3569 int fd;
3570 int pgrp;
3571
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003572 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003573 return;
3574 if (on) {
3575 int ofd;
3576 ofd = fd = open(_PATH_TTY, O_RDWR);
3577 if (fd < 0) {
3578 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3579 * That sometimes helps to acquire controlling tty.
3580 * Obviously, a workaround for bugs when someone
3581 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003582 fd = 2;
3583 while (!isatty(fd))
3584 if (--fd < 0)
3585 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003586 }
3587 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003588 if (ofd >= 0)
3589 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003590 if (fd < 0)
3591 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003592 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003593 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003594 do { /* while we are in the background */
3595 pgrp = tcgetpgrp(fd);
3596 if (pgrp < 0) {
3597 out:
3598 ash_msg("can't access tty; job control turned off");
3599 mflag = on = 0;
3600 goto close;
3601 }
3602 if (pgrp == getpgrp())
3603 break;
3604 killpg(0, SIGTTIN);
3605 } while (1);
3606 initialpgrp = pgrp;
3607
3608 setsignal(SIGTSTP);
3609 setsignal(SIGTTOU);
3610 setsignal(SIGTTIN);
3611 pgrp = rootpid;
3612 setpgid(0, pgrp);
3613 xtcsetpgrp(fd, pgrp);
3614 } else {
3615 /* turning job control off */
3616 fd = ttyfd;
3617 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003618 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003619 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003620 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003621 setpgid(0, pgrp);
3622 setsignal(SIGTSTP);
3623 setsignal(SIGTTOU);
3624 setsignal(SIGTTIN);
3625 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003626 if (fd >= 0)
3627 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003628 fd = -1;
3629 }
3630 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003631 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003632}
3633
3634static int
3635killcmd(int argc, char **argv)
3636{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003637 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003638 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003639 do {
3640 if (argv[i][0] == '%') {
3641 struct job *jp = getjob(argv[i], 0);
3642 unsigned pid = jp->ps[0].pid;
3643 /* Enough space for ' -NNN<nul>' */
3644 argv[i] = alloca(sizeof(int)*3 + 3);
3645 /* kill_main has matching code to expect
3646 * leading space. Needed to not confuse
3647 * negative pids with "kill -SIGNAL_NO" syntax */
3648 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003649 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003650 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003651 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003652 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003653}
3654
3655static void
3656showpipe(struct job *jp, FILE *out)
3657{
3658 struct procstat *sp;
3659 struct procstat *spend;
3660
3661 spend = jp->ps + jp->nprocs;
3662 for (sp = jp->ps + 1; sp < spend; sp++)
3663 fprintf(out, " | %s", sp->cmd);
3664 outcslow('\n', out);
3665 flush_stdout_stderr();
3666}
3667
3668
3669static int
3670restartjob(struct job *jp, int mode)
3671{
3672 struct procstat *ps;
3673 int i;
3674 int status;
3675 pid_t pgid;
3676
3677 INT_OFF;
3678 if (jp->state == JOBDONE)
3679 goto out;
3680 jp->state = JOBRUNNING;
3681 pgid = jp->ps->pid;
3682 if (mode == FORK_FG)
3683 xtcsetpgrp(ttyfd, pgid);
3684 killpg(pgid, SIGCONT);
3685 ps = jp->ps;
3686 i = jp->nprocs;
3687 do {
3688 if (WIFSTOPPED(ps->status)) {
3689 ps->status = -1;
3690 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003691 ps++;
3692 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003693 out:
3694 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3695 INT_ON;
3696 return status;
3697}
3698
3699static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003700fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003701{
3702 struct job *jp;
3703 FILE *out;
3704 int mode;
3705 int retval;
3706
3707 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3708 nextopt(nullstr);
3709 argv = argptr;
3710 out = stdout;
3711 do {
3712 jp = getjob(*argv, 1);
3713 if (mode == FORK_BG) {
3714 set_curjob(jp, CUR_RUNNING);
3715 fprintf(out, "[%d] ", jobno(jp));
3716 }
3717 outstr(jp->ps->cmd, out);
3718 showpipe(jp, out);
3719 retval = restartjob(jp, mode);
3720 } while (*argv && *++argv);
3721 return retval;
3722}
3723#endif
3724
3725static int
3726sprint_status(char *s, int status, int sigonly)
3727{
3728 int col;
3729 int st;
3730
3731 col = 0;
3732 if (!WIFEXITED(status)) {
3733#if JOBS
3734 if (WIFSTOPPED(status))
3735 st = WSTOPSIG(status);
3736 else
3737#endif
3738 st = WTERMSIG(status);
3739 if (sigonly) {
3740 if (st == SIGINT || st == SIGPIPE)
3741 goto out;
3742#if JOBS
3743 if (WIFSTOPPED(status))
3744 goto out;
3745#endif
3746 }
3747 st &= 0x7f;
3748 col = fmtstr(s, 32, strsignal(st));
3749 if (WCOREDUMP(status)) {
3750 col += fmtstr(s + col, 16, " (core dumped)");
3751 }
3752 } else if (!sigonly) {
3753 st = WEXITSTATUS(status);
3754 if (st)
3755 col = fmtstr(s, 16, "Done(%d)", st);
3756 else
3757 col = fmtstr(s, 16, "Done");
3758 }
3759 out:
3760 return col;
3761}
3762
3763/*
3764 * Do a wait system call. If job control is compiled in, we accept
3765 * stopped processes. If block is zero, we return a value of zero
3766 * rather than blocking.
3767 *
3768 * System V doesn't have a non-blocking wait system call. It does
3769 * have a SIGCLD signal that is sent to a process when one of it's
3770 * children dies. The obvious way to use SIGCLD would be to install
3771 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3772 * was received, and have waitproc bump another counter when it got
3773 * the status of a process. Waitproc would then know that a wait
3774 * system call would not block if the two counters were different.
3775 * This approach doesn't work because if a process has children that
3776 * have not been waited for, System V will send it a SIGCLD when it
3777 * installs a signal handler for SIGCLD. What this means is that when
3778 * a child exits, the shell will be sent SIGCLD signals continuously
3779 * until is runs out of stack space, unless it does a wait call before
3780 * restoring the signal handler. The code below takes advantage of
3781 * this (mis)feature by installing a signal handler for SIGCLD and
3782 * then checking to see whether it was called. If there are any
3783 * children to be waited for, it will be.
3784 *
3785 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3786 * waits at all. In this case, the user will not be informed when
3787 * a background process until the next time she runs a real program
3788 * (as opposed to running a builtin command or just typing return),
3789 * and the jobs command may give out of date information.
3790 */
3791static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003792waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003793{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003794#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003795 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003796 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003797#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003798 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3799 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800}
3801
3802/*
3803 * Wait for a process to terminate.
3804 */
3805static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003806dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003807{
3808 int pid;
3809 int status;
3810 struct job *jp;
3811 struct job *thisjob;
3812 int state;
3813
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003814 TRACE(("dowait(%d) called\n", wait_flags));
3815 pid = waitproc(wait_flags, &status);
3816 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003817 if (pid <= 0) {
3818 /* If we were doing blocking wait and (probably) got EINTR,
3819 * check for pending sigs received while waiting.
3820 * (NB: can be moved into callers if needed) */
3821 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3822 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003823 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003824 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003825 INT_OFF;
3826 thisjob = NULL;
3827 for (jp = curjob; jp; jp = jp->prev_job) {
3828 struct procstat *sp;
3829 struct procstat *spend;
3830 if (jp->state == JOBDONE)
3831 continue;
3832 state = JOBDONE;
3833 spend = jp->ps + jp->nprocs;
3834 sp = jp->ps;
3835 do {
3836 if (sp->pid == pid) {
3837 TRACE(("Job %d: changing status of proc %d "
3838 "from 0x%x to 0x%x\n",
3839 jobno(jp), pid, sp->status, status));
3840 sp->status = status;
3841 thisjob = jp;
3842 }
3843 if (sp->status == -1)
3844 state = JOBRUNNING;
3845#if JOBS
3846 if (state == JOBRUNNING)
3847 continue;
3848 if (WIFSTOPPED(sp->status)) {
3849 jp->stopstatus = sp->status;
3850 state = JOBSTOPPED;
3851 }
3852#endif
3853 } while (++sp < spend);
3854 if (thisjob)
3855 goto gotjob;
3856 }
3857#if JOBS
3858 if (!WIFSTOPPED(status))
3859#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003860 jobless--;
3861 goto out;
3862
3863 gotjob:
3864 if (state != JOBRUNNING) {
3865 thisjob->changed = 1;
3866
3867 if (thisjob->state != state) {
3868 TRACE(("Job %d: changing state from %d to %d\n",
3869 jobno(thisjob), thisjob->state, state));
3870 thisjob->state = state;
3871#if JOBS
3872 if (state == JOBSTOPPED) {
3873 set_curjob(thisjob, CUR_STOPPED);
3874 }
3875#endif
3876 }
3877 }
3878
3879 out:
3880 INT_ON;
3881
3882 if (thisjob && thisjob == job) {
3883 char s[48 + 1];
3884 int len;
3885
3886 len = sprint_status(s, status, 1);
3887 if (len) {
3888 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003889 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003890 out2str(s);
3891 }
3892 }
3893 return pid;
3894}
3895
3896#if JOBS
3897static void
3898showjob(FILE *out, struct job *jp, int mode)
3899{
3900 struct procstat *ps;
3901 struct procstat *psend;
3902 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003903 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003904 char s[80];
3905
3906 ps = jp->ps;
3907
3908 if (mode & SHOW_PGID) {
3909 /* just output process (group) id of pipeline */
3910 fprintf(out, "%d\n", ps->pid);
3911 return;
3912 }
3913
3914 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003915 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003916
3917 if (jp == curjob)
3918 s[col - 2] = '+';
3919 else if (curjob && jp == curjob->prev_job)
3920 s[col - 2] = '-';
3921
3922 if (mode & SHOW_PID)
3923 col += fmtstr(s + col, 16, "%d ", ps->pid);
3924
3925 psend = ps + jp->nprocs;
3926
3927 if (jp->state == JOBRUNNING) {
3928 strcpy(s + col, "Running");
3929 col += sizeof("Running") - 1;
3930 } else {
3931 int status = psend[-1].status;
3932 if (jp->state == JOBSTOPPED)
3933 status = jp->stopstatus;
3934 col += sprint_status(s + col, status, 0);
3935 }
3936
3937 goto start;
3938
3939 do {
3940 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003941 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003942 start:
3943 fprintf(out, "%s%*c%s",
3944 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3945 );
3946 if (!(mode & SHOW_PID)) {
3947 showpipe(jp, out);
3948 break;
3949 }
3950 if (++ps == psend) {
3951 outcslow('\n', out);
3952 break;
3953 }
3954 } while (1);
3955
3956 jp->changed = 0;
3957
3958 if (jp->state == JOBDONE) {
3959 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3960 freejob(jp);
3961 }
3962}
3963
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964/*
3965 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3966 * statuses have changed since the last call to showjobs.
3967 */
3968static void
3969showjobs(FILE *out, int mode)
3970{
3971 struct job *jp;
3972
3973 TRACE(("showjobs(%x) called\n", mode));
3974
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003975 /* If not even one job changed, there is nothing to do */
3976 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003977 continue;
3978
3979 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003980 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003981 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003982 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003983 }
3984}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003985
3986static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003987jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003988{
3989 int mode, m;
3990
3991 mode = 0;
3992 while ((m = nextopt("lp"))) {
3993 if (m == 'l')
3994 mode = SHOW_PID;
3995 else
3996 mode = SHOW_PGID;
3997 }
3998
3999 argv = argptr;
4000 if (*argv) {
4001 do
4002 showjob(stdout, getjob(*argv,0), mode);
4003 while (*++argv);
4004 } else
4005 showjobs(stdout, mode);
4006
4007 return 0;
4008}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004009#endif /* JOBS */
4010
4011static int
4012getstatus(struct job *job)
4013{
4014 int status;
4015 int retval;
4016
4017 status = job->ps[job->nprocs - 1].status;
4018 retval = WEXITSTATUS(status);
4019 if (!WIFEXITED(status)) {
4020#if JOBS
4021 retval = WSTOPSIG(status);
4022 if (!WIFSTOPPED(status))
4023#endif
4024 {
4025 /* XXX: limits number of signals */
4026 retval = WTERMSIG(status);
4027#if JOBS
4028 if (retval == SIGINT)
4029 job->sigint = 1;
4030#endif
4031 }
4032 retval += 128;
4033 }
4034 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4035 jobno(job), job->nprocs, status, retval));
4036 return retval;
4037}
4038
4039static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004040waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041{
4042 struct job *job;
4043 int retval;
4044 struct job *jp;
4045
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004046// exsig++;
4047// xbarrier();
4048 if (pendingsig)
4049 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050
4051 nextopt(nullstr);
4052 retval = 0;
4053
4054 argv = argptr;
4055 if (!*argv) {
4056 /* wait for all jobs */
4057 for (;;) {
4058 jp = curjob;
4059 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004060 if (!jp) /* no running procs */
4061 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004062 if (jp->state == JOBRUNNING)
4063 break;
4064 jp->waited = 1;
4065 jp = jp->prev_job;
4066 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004067 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068 }
4069 }
4070
4071 retval = 127;
4072 do {
4073 if (**argv != '%') {
4074 pid_t pid = number(*argv);
4075 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004076 while (1) {
4077 if (!job)
4078 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004079 if (job->ps[job->nprocs - 1].pid == pid)
4080 break;
4081 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004082 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004083 } else
4084 job = getjob(*argv, 0);
4085 /* loop until process terminated or stopped */
4086 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004087 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 job->waited = 1;
4089 retval = getstatus(job);
4090 repeat:
4091 ;
4092 } while (*++argv);
4093
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004094 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004095 return retval;
4096}
4097
4098static struct job *
4099growjobtab(void)
4100{
4101 size_t len;
4102 ptrdiff_t offset;
4103 struct job *jp, *jq;
4104
4105 len = njobs * sizeof(*jp);
4106 jq = jobtab;
4107 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4108
4109 offset = (char *)jp - (char *)jq;
4110 if (offset) {
4111 /* Relocate pointers */
4112 size_t l = len;
4113
4114 jq = (struct job *)((char *)jq + l);
4115 while (l) {
4116 l -= sizeof(*jp);
4117 jq--;
4118#define joff(p) ((struct job *)((char *)(p) + l))
4119#define jmove(p) (p) = (void *)((char *)(p) + offset)
4120 if (joff(jp)->ps == &jq->ps0)
4121 jmove(joff(jp)->ps);
4122 if (joff(jp)->prev_job)
4123 jmove(joff(jp)->prev_job);
4124 }
4125 if (curjob)
4126 jmove(curjob);
4127#undef joff
4128#undef jmove
4129 }
4130
4131 njobs += 4;
4132 jobtab = jp;
4133 jp = (struct job *)((char *)jp + len);
4134 jq = jp + 3;
4135 do {
4136 jq->used = 0;
4137 } while (--jq >= jp);
4138 return jp;
4139}
4140
4141/*
4142 * Return a new job structure.
4143 * Called with interrupts off.
4144 */
4145static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004146makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004147{
4148 int i;
4149 struct job *jp;
4150
4151 for (i = njobs, jp = jobtab; ; jp++) {
4152 if (--i < 0) {
4153 jp = growjobtab();
4154 break;
4155 }
4156 if (jp->used == 0)
4157 break;
4158 if (jp->state != JOBDONE || !jp->waited)
4159 continue;
4160#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004161 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004162 continue;
4163#endif
4164 freejob(jp);
4165 break;
4166 }
4167 memset(jp, 0, sizeof(*jp));
4168#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004169 /* jp->jobctl is a bitfield.
4170 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004171 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004172 jp->jobctl = 1;
4173#endif
4174 jp->prev_job = curjob;
4175 curjob = jp;
4176 jp->used = 1;
4177 jp->ps = &jp->ps0;
4178 if (nprocs > 1) {
4179 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4180 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004181 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004182 jobno(jp)));
4183 return jp;
4184}
4185
4186#if JOBS
4187/*
4188 * Return a string identifying a command (to be printed by the
4189 * jobs command).
4190 */
4191static char *cmdnextc;
4192
4193static void
4194cmdputs(const char *s)
4195{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004196 static const char vstype[VSTYPE + 1][3] = {
4197 "", "}", "-", "+", "?", "=",
4198 "%", "%%", "#", "##"
4199 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4200 };
4201
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004202 const char *p, *str;
4203 char c, cc[2] = " ";
4204 char *nextc;
4205 int subtype = 0;
4206 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004207
4208 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4209 p = s;
4210 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004211 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004212 switch (c) {
4213 case CTLESC:
4214 c = *p++;
4215 break;
4216 case CTLVAR:
4217 subtype = *p++;
4218 if ((subtype & VSTYPE) == VSLENGTH)
4219 str = "${#";
4220 else
4221 str = "${";
4222 if (!(subtype & VSQUOTE) == !(quoted & 1))
4223 goto dostr;
4224 quoted ^= 1;
4225 c = '"';
4226 break;
4227 case CTLENDVAR:
4228 str = "\"}" + !(quoted & 1);
4229 quoted >>= 1;
4230 subtype = 0;
4231 goto dostr;
4232 case CTLBACKQ:
4233 str = "$(...)";
4234 goto dostr;
4235 case CTLBACKQ+CTLQUOTE:
4236 str = "\"$(...)\"";
4237 goto dostr;
4238#if ENABLE_ASH_MATH_SUPPORT
4239 case CTLARI:
4240 str = "$((";
4241 goto dostr;
4242 case CTLENDARI:
4243 str = "))";
4244 goto dostr;
4245#endif
4246 case CTLQUOTEMARK:
4247 quoted ^= 1;
4248 c = '"';
4249 break;
4250 case '=':
4251 if (subtype == 0)
4252 break;
4253 if ((subtype & VSTYPE) != VSNORMAL)
4254 quoted <<= 1;
4255 str = vstype[subtype & VSTYPE];
4256 if (subtype & VSNUL)
4257 c = ':';
4258 else
4259 goto checkstr;
4260 break;
4261 case '\'':
4262 case '\\':
4263 case '"':
4264 case '$':
4265 /* These can only happen inside quotes */
4266 cc[0] = c;
4267 str = cc;
4268 c = '\\';
4269 break;
4270 default:
4271 break;
4272 }
4273 USTPUTC(c, nextc);
4274 checkstr:
4275 if (!str)
4276 continue;
4277 dostr:
4278 while ((c = *str++)) {
4279 USTPUTC(c, nextc);
4280 }
4281 }
4282 if (quoted & 1) {
4283 USTPUTC('"', nextc);
4284 }
4285 *nextc = 0;
4286 cmdnextc = nextc;
4287}
4288
4289/* cmdtxt() and cmdlist() call each other */
4290static void cmdtxt(union node *n);
4291
4292static void
4293cmdlist(union node *np, int sep)
4294{
4295 for (; np; np = np->narg.next) {
4296 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004297 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004298 cmdtxt(np);
4299 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004300 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004301 }
4302}
4303
4304static void
4305cmdtxt(union node *n)
4306{
4307 union node *np;
4308 struct nodelist *lp;
4309 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004310
4311 if (!n)
4312 return;
4313 switch (n->type) {
4314 default:
4315#if DEBUG
4316 abort();
4317#endif
4318 case NPIPE:
4319 lp = n->npipe.cmdlist;
4320 for (;;) {
4321 cmdtxt(lp->n);
4322 lp = lp->next;
4323 if (!lp)
4324 break;
4325 cmdputs(" | ");
4326 }
4327 break;
4328 case NSEMI:
4329 p = "; ";
4330 goto binop;
4331 case NAND:
4332 p = " && ";
4333 goto binop;
4334 case NOR:
4335 p = " || ";
4336 binop:
4337 cmdtxt(n->nbinary.ch1);
4338 cmdputs(p);
4339 n = n->nbinary.ch2;
4340 goto donode;
4341 case NREDIR:
4342 case NBACKGND:
4343 n = n->nredir.n;
4344 goto donode;
4345 case NNOT:
4346 cmdputs("!");
4347 n = n->nnot.com;
4348 donode:
4349 cmdtxt(n);
4350 break;
4351 case NIF:
4352 cmdputs("if ");
4353 cmdtxt(n->nif.test);
4354 cmdputs("; then ");
4355 n = n->nif.ifpart;
4356 if (n->nif.elsepart) {
4357 cmdtxt(n);
4358 cmdputs("; else ");
4359 n = n->nif.elsepart;
4360 }
4361 p = "; fi";
4362 goto dotail;
4363 case NSUBSHELL:
4364 cmdputs("(");
4365 n = n->nredir.n;
4366 p = ")";
4367 goto dotail;
4368 case NWHILE:
4369 p = "while ";
4370 goto until;
4371 case NUNTIL:
4372 p = "until ";
4373 until:
4374 cmdputs(p);
4375 cmdtxt(n->nbinary.ch1);
4376 n = n->nbinary.ch2;
4377 p = "; done";
4378 dodo:
4379 cmdputs("; do ");
4380 dotail:
4381 cmdtxt(n);
4382 goto dotail2;
4383 case NFOR:
4384 cmdputs("for ");
4385 cmdputs(n->nfor.var);
4386 cmdputs(" in ");
4387 cmdlist(n->nfor.args, 1);
4388 n = n->nfor.body;
4389 p = "; done";
4390 goto dodo;
4391 case NDEFUN:
4392 cmdputs(n->narg.text);
4393 p = "() { ... }";
4394 goto dotail2;
4395 case NCMD:
4396 cmdlist(n->ncmd.args, 1);
4397 cmdlist(n->ncmd.redirect, 0);
4398 break;
4399 case NARG:
4400 p = n->narg.text;
4401 dotail2:
4402 cmdputs(p);
4403 break;
4404 case NHERE:
4405 case NXHERE:
4406 p = "<<...";
4407 goto dotail2;
4408 case NCASE:
4409 cmdputs("case ");
4410 cmdputs(n->ncase.expr->narg.text);
4411 cmdputs(" in ");
4412 for (np = n->ncase.cases; np; np = np->nclist.next) {
4413 cmdtxt(np->nclist.pattern);
4414 cmdputs(") ");
4415 cmdtxt(np->nclist.body);
4416 cmdputs(";; ");
4417 }
4418 p = "esac";
4419 goto dotail2;
4420 case NTO:
4421 p = ">";
4422 goto redir;
4423 case NCLOBBER:
4424 p = ">|";
4425 goto redir;
4426 case NAPPEND:
4427 p = ">>";
4428 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004429#if ENABLE_ASH_BASH_COMPAT
4430 case NTO2:
4431#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004432 case NTOFD:
4433 p = ">&";
4434 goto redir;
4435 case NFROM:
4436 p = "<";
4437 goto redir;
4438 case NFROMFD:
4439 p = "<&";
4440 goto redir;
4441 case NFROMTO:
4442 p = "<>";
4443 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004444 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004445 cmdputs(p);
4446 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004447 cmdputs(utoa(n->ndup.dupfd));
4448 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004449 }
4450 n = n->nfile.fname;
4451 goto donode;
4452 }
4453}
4454
4455static char *
4456commandtext(union node *n)
4457{
4458 char *name;
4459
4460 STARTSTACKSTR(cmdnextc);
4461 cmdtxt(n);
4462 name = stackblock();
4463 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4464 name, cmdnextc, cmdnextc));
4465 return ckstrdup(name);
4466}
4467#endif /* JOBS */
4468
4469/*
4470 * Fork off a subshell. If we are doing job control, give the subshell its
4471 * own process group. Jp is a job structure that the job is to be added to.
4472 * N is the command that will be evaluated by the child. Both jp and n may
4473 * be NULL. The mode parameter can be one of the following:
4474 * FORK_FG - Fork off a foreground process.
4475 * FORK_BG - Fork off a background process.
4476 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4477 * process group even if job control is on.
4478 *
4479 * When job control is turned off, background processes have their standard
4480 * input redirected to /dev/null (except for the second and later processes
4481 * in a pipeline).
4482 *
4483 * Called with interrupts off.
4484 */
4485/*
4486 * Clear traps on a fork.
4487 */
4488static void
4489clear_traps(void)
4490{
4491 char **tp;
4492
4493 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004494 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004495 INT_OFF;
4496 free(*tp);
4497 *tp = NULL;
4498 if (tp != &trap[0])
4499 setsignal(tp - trap);
4500 INT_ON;
4501 }
4502 }
4503}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004504
4505/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004506static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004507
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004508/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004509static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004510forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004511{
4512 int oldlvl;
4513
4514 TRACE(("Child shell %d\n", getpid()));
4515 oldlvl = shlvl;
4516 shlvl++;
4517
4518 closescript();
4519 clear_traps();
4520#if JOBS
4521 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004522 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004523 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4524 pid_t pgrp;
4525
4526 if (jp->nprocs == 0)
4527 pgrp = getpid();
4528 else
4529 pgrp = jp->ps[0].pid;
4530 /* This can fail because we are doing it in the parent also */
4531 (void)setpgid(0, pgrp);
4532 if (mode == FORK_FG)
4533 xtcsetpgrp(ttyfd, pgrp);
4534 setsignal(SIGTSTP);
4535 setsignal(SIGTTOU);
4536 } else
4537#endif
4538 if (mode == FORK_BG) {
4539 ignoresig(SIGINT);
4540 ignoresig(SIGQUIT);
4541 if (jp->nprocs == 0) {
4542 close(0);
4543 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004544 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004545 }
4546 }
4547 if (!oldlvl && iflag) {
4548 setsignal(SIGINT);
4549 setsignal(SIGQUIT);
4550 setsignal(SIGTERM);
4551 }
4552 for (jp = curjob; jp; jp = jp->prev_job)
4553 freejob(jp);
4554 jobless = 0;
4555}
4556
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004557/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004558#if !JOBS
4559#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4560#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004561static void
4562forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4563{
4564 TRACE(("In parent shell: child = %d\n", pid));
4565 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004566 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4567 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004568 jobless++;
4569 return;
4570 }
4571#if JOBS
4572 if (mode != FORK_NOJOB && jp->jobctl) {
4573 int pgrp;
4574
4575 if (jp->nprocs == 0)
4576 pgrp = pid;
4577 else
4578 pgrp = jp->ps[0].pid;
4579 /* This can fail because we are doing it in the child also */
4580 setpgid(pid, pgrp);
4581 }
4582#endif
4583 if (mode == FORK_BG) {
4584 backgndpid = pid; /* set $! */
4585 set_curjob(jp, CUR_RUNNING);
4586 }
4587 if (jp) {
4588 struct procstat *ps = &jp->ps[jp->nprocs++];
4589 ps->pid = pid;
4590 ps->status = -1;
4591 ps->cmd = nullstr;
4592#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004593 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004594 ps->cmd = commandtext(n);
4595#endif
4596 }
4597}
4598
4599static int
4600forkshell(struct job *jp, union node *n, int mode)
4601{
4602 int pid;
4603
4604 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4605 pid = fork();
4606 if (pid < 0) {
4607 TRACE(("Fork failed, errno=%d", errno));
4608 if (jp)
4609 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004610 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004611 }
4612 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004613 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004614 else
4615 forkparent(jp, n, mode, pid);
4616 return pid;
4617}
4618
4619/*
4620 * Wait for job to finish.
4621 *
4622 * Under job control we have the problem that while a child process is
4623 * running interrupts generated by the user are sent to the child but not
4624 * to the shell. This means that an infinite loop started by an inter-
4625 * active user may be hard to kill. With job control turned off, an
4626 * interactive user may place an interactive program inside a loop. If
4627 * the interactive program catches interrupts, the user doesn't want
4628 * these interrupts to also abort the loop. The approach we take here
4629 * is to have the shell ignore interrupt signals while waiting for a
4630 * foreground process to terminate, and then send itself an interrupt
4631 * signal if the child process was terminated by an interrupt signal.
4632 * Unfortunately, some programs want to do a bit of cleanup and then
4633 * exit on interrupt; unless these processes terminate themselves by
4634 * sending a signal to themselves (instead of calling exit) they will
4635 * confuse this approach.
4636 *
4637 * Called with interrupts off.
4638 */
4639static int
4640waitforjob(struct job *jp)
4641{
4642 int st;
4643
4644 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4645 while (jp->state == JOBRUNNING) {
4646 dowait(DOWAIT_BLOCK, jp);
4647 }
4648 st = getstatus(jp);
4649#if JOBS
4650 if (jp->jobctl) {
4651 xtcsetpgrp(ttyfd, rootpid);
4652 /*
4653 * This is truly gross.
4654 * If we're doing job control, then we did a TIOCSPGRP which
4655 * caused us (the shell) to no longer be in the controlling
4656 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4657 * intuit from the subprocess exit status whether a SIGINT
4658 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4659 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004660 if (jp->sigint) /* TODO: do the same with all signals */
4661 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004662 }
4663 if (jp->state == JOBDONE)
4664#endif
4665 freejob(jp);
4666 return st;
4667}
4668
4669/*
4670 * return 1 if there are stopped jobs, otherwise 0
4671 */
4672static int
4673stoppedjobs(void)
4674{
4675 struct job *jp;
4676 int retval;
4677
4678 retval = 0;
4679 if (job_warning)
4680 goto out;
4681 jp = curjob;
4682 if (jp && jp->state == JOBSTOPPED) {
4683 out2str("You have stopped jobs.\n");
4684 job_warning = 2;
4685 retval++;
4686 }
4687 out:
4688 return retval;
4689}
4690
4691
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004692/* ============ redir.c
4693 *
4694 * Code for dealing with input/output redirection.
4695 */
4696
4697#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004698#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004699
4700/*
4701 * Open a file in noclobber mode.
4702 * The code was copied from bash.
4703 */
4704static int
4705noclobberopen(const char *fname)
4706{
4707 int r, fd;
4708 struct stat finfo, finfo2;
4709
4710 /*
4711 * If the file exists and is a regular file, return an error
4712 * immediately.
4713 */
4714 r = stat(fname, &finfo);
4715 if (r == 0 && S_ISREG(finfo.st_mode)) {
4716 errno = EEXIST;
4717 return -1;
4718 }
4719
4720 /*
4721 * If the file was not present (r != 0), make sure we open it
4722 * exclusively so that if it is created before we open it, our open
4723 * will fail. Make sure that we do not truncate an existing file.
4724 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4725 * file was not a regular file, we leave O_EXCL off.
4726 */
4727 if (r != 0)
4728 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4729 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4730
4731 /* If the open failed, return the file descriptor right away. */
4732 if (fd < 0)
4733 return fd;
4734
4735 /*
4736 * OK, the open succeeded, but the file may have been changed from a
4737 * non-regular file to a regular file between the stat and the open.
4738 * We are assuming that the O_EXCL open handles the case where FILENAME
4739 * did not exist and is symlinked to an existing file between the stat
4740 * and open.
4741 */
4742
4743 /*
4744 * If we can open it and fstat the file descriptor, and neither check
4745 * revealed that it was a regular file, and the file has not been
4746 * replaced, return the file descriptor.
4747 */
4748 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4749 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4750 return fd;
4751
4752 /* The file has been replaced. badness. */
4753 close(fd);
4754 errno = EEXIST;
4755 return -1;
4756}
4757
4758/*
4759 * Handle here documents. Normally we fork off a process to write the
4760 * data to a pipe. If the document is short, we can stuff the data in
4761 * the pipe without forking.
4762 */
4763/* openhere needs this forward reference */
4764static void expandhere(union node *arg, int fd);
4765static int
4766openhere(union node *redir)
4767{
4768 int pip[2];
4769 size_t len = 0;
4770
4771 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004772 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004773 if (redir->type == NHERE) {
4774 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004775 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004776 full_write(pip[1], redir->nhere.doc->narg.text, len);
4777 goto out;
4778 }
4779 }
4780 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004781 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004782 close(pip[0]);
4783 signal(SIGINT, SIG_IGN);
4784 signal(SIGQUIT, SIG_IGN);
4785 signal(SIGHUP, SIG_IGN);
4786#ifdef SIGTSTP
4787 signal(SIGTSTP, SIG_IGN);
4788#endif
4789 signal(SIGPIPE, SIG_DFL);
4790 if (redir->type == NHERE)
4791 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004792 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004793 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004794 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004795 }
4796 out:
4797 close(pip[1]);
4798 return pip[0];
4799}
4800
4801static int
4802openredirect(union node *redir)
4803{
4804 char *fname;
4805 int f;
4806
4807 switch (redir->nfile.type) {
4808 case NFROM:
4809 fname = redir->nfile.expfname;
4810 f = open(fname, O_RDONLY);
4811 if (f < 0)
4812 goto eopen;
4813 break;
4814 case NFROMTO:
4815 fname = redir->nfile.expfname;
4816 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4817 if (f < 0)
4818 goto ecreate;
4819 break;
4820 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004821#if ENABLE_ASH_BASH_COMPAT
4822 case NTO2:
4823#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004824 /* Take care of noclobber mode. */
4825 if (Cflag) {
4826 fname = redir->nfile.expfname;
4827 f = noclobberopen(fname);
4828 if (f < 0)
4829 goto ecreate;
4830 break;
4831 }
4832 /* FALLTHROUGH */
4833 case NCLOBBER:
4834 fname = redir->nfile.expfname;
4835 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4836 if (f < 0)
4837 goto ecreate;
4838 break;
4839 case NAPPEND:
4840 fname = redir->nfile.expfname;
4841 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4842 if (f < 0)
4843 goto ecreate;
4844 break;
4845 default:
4846#if DEBUG
4847 abort();
4848#endif
4849 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004850/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004851// case NTOFD:
4852// case NFROMFD:
4853// f = -1;
4854// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004855 case NHERE:
4856 case NXHERE:
4857 f = openhere(redir);
4858 break;
4859 }
4860
4861 return f;
4862 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004863 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004864 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004865 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004866}
4867
4868/*
4869 * Copy a file descriptor to be >= to. Returns -1
4870 * if the source file descriptor is closed, EMPTY if there are no unused
4871 * file descriptors left.
4872 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004873/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4874 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004875enum {
4876 COPYFD_EXACT = (int)~(INT_MAX),
4877 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4878};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004879static int
4880copyfd(int from, int to)
4881{
4882 int newfd;
4883
Denis Vlasenko5a867312008-07-24 19:46:38 +00004884 if (to & COPYFD_EXACT) {
4885 to &= ~COPYFD_EXACT;
4886 /*if (from != to)*/
4887 newfd = dup2(from, to);
4888 } else {
4889 newfd = fcntl(from, F_DUPFD, to);
4890 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004891 if (newfd < 0) {
4892 if (errno == EMFILE)
4893 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004894 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004895 ash_msg_and_raise_error("%d: %m", from);
4896 }
4897 return newfd;
4898}
4899
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004900/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004901struct two_fd_t {
4902 int orig, copy;
4903};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004904struct redirtab {
4905 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004906 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004907 int pair_count;
4908 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004909};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004910#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004911
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004912static int need_to_remember(struct redirtab *rp, int fd)
4913{
4914 int i;
4915
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004916 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004917 return 0;
4918
4919 for (i = 0; i < rp->pair_count; i++) {
4920 if (rp->two_fd[i].orig == fd) {
4921 /* already remembered */
4922 return 0;
4923 }
4924 }
4925 return 1;
4926}
4927
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004928/* "hidden" fd is a fd used to read scripts, or a copy of such */
4929static int is_hidden_fd(struct redirtab *rp, int fd)
4930{
4931 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004932 struct parsefile *pf;
4933
4934 if (fd == -1)
4935 return 0;
4936 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004937 while (pf) {
4938 if (fd == pf->fd) {
4939 return 1;
4940 }
4941 pf = pf->prev;
4942 }
4943 if (!rp)
4944 return 0;
4945 fd |= COPYFD_RESTORE;
4946 for (i = 0; i < rp->pair_count; i++) {
4947 if (rp->two_fd[i].copy == fd) {
4948 return 1;
4949 }
4950 }
4951 return 0;
4952}
4953
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004954/*
4955 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4956 * old file descriptors are stashed away so that the redirection can be
4957 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4958 * standard output, and the standard error if it becomes a duplicate of
4959 * stdout, is saved in memory.
4960 */
4961/* flags passed to redirect */
4962#define REDIR_PUSH 01 /* save previous values of file descriptors */
4963#define REDIR_SAVEFD2 03 /* set preverrout */
4964static void
4965redirect(union node *redir, int flags)
4966{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004967 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004968 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004969 int i;
4970 int fd;
4971 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004972 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004973
Denis Vlasenko01631112007-12-16 17:20:38 +00004974 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004975 if (!redir) {
4976 return;
4977 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004978
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004979 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004980 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004981 INT_OFF;
4982 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004983 union node *tmp = redir;
4984 do {
4985 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004986#if ENABLE_ASH_BASH_COMPAT
4987 if (redir->nfile.type == NTO2)
4988 sv_pos++;
4989#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004990 tmp = tmp->nfile.next;
4991 } while (tmp);
4992 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004993 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004994 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004995 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004996 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004997 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004998 while (sv_pos > 0) {
4999 sv_pos--;
5000 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5001 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005002 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005003
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005004 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005005 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005006 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005007 int right_fd = redir->ndup.dupfd;
5008 /* redirect from/to same file descriptor? */
5009 if (right_fd == fd)
5010 continue;
5011 /* echo >&10 and 10 is a fd opened to the sh script? */
5012 if (is_hidden_fd(sv, right_fd)) {
5013 errno = EBADF; /* as if it is closed */
5014 ash_msg_and_raise_error("%d: %m", right_fd);
5015 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005016 newfd = -1;
5017 } else {
5018 newfd = openredirect(redir); /* always >= 0 */
5019 if (fd == newfd) {
5020 /* Descriptor wasn't open before redirect.
5021 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005022 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005023 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005024 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005025 continue;
5026 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005027 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005028#if ENABLE_ASH_BASH_COMPAT
5029 redirect_more:
5030#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005031 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005032 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005033 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005034/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5035 * are closed in popredir() in the child, preventing them from leaking
5036 * into child. (popredir() also cleans up the mess in case of failures)
5037 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005038 if (i == -1) {
5039 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005040 if (i != EBADF) {
5041 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005042 if (newfd >= 0)
5043 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005044 errno = i;
5045 ash_msg_and_raise_error("%d: %m", fd);
5046 /* NOTREACHED */
5047 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005048 /* EBADF: it is not open - good, remember to close it */
5049 remember_to_close:
5050 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005051 } else { /* fd is open, save its copy */
5052 /* "exec fd>&-" should not close fds
5053 * which point to script file(s).
5054 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005055 if (is_hidden_fd(sv, fd))
5056 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005057 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005058 if (fd == 2)
5059 copied_fd2 = i;
5060 sv->two_fd[sv_pos].orig = fd;
5061 sv->two_fd[sv_pos].copy = i;
5062 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005063 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005064 if (newfd < 0) {
5065 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005066 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005067 close(fd);
5068 } else {
5069 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005070 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005071 } else if (fd != newfd) { /* move newfd to fd */
5072 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005073#if ENABLE_ASH_BASH_COMPAT
5074 if (!(redir->nfile.type == NTO2 && fd == 2))
5075#endif
5076 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005077 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005078#if ENABLE_ASH_BASH_COMPAT
5079 if (redir->nfile.type == NTO2 && fd == 1) {
5080 /* We already redirected it to fd 1, now copy it to 2 */
5081 newfd = 1;
5082 fd = 2;
5083 goto redirect_more;
5084 }
5085#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005086 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005087
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005088 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005089 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5090 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005091}
5092
5093/*
5094 * Undo the effects of the last redirection.
5095 */
5096static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005097popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005098{
5099 struct redirtab *rp;
5100 int i;
5101
Denis Vlasenko01631112007-12-16 17:20:38 +00005102 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005103 return;
5104 INT_OFF;
5105 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005106 for (i = 0; i < rp->pair_count; i++) {
5107 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005108 int copy = rp->two_fd[i].copy;
5109 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005110 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005111 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005112 continue;
5113 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005114 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005115 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005116 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005117 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005118 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005119 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005120 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005121 }
5122 }
5123 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005124 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005125 free(rp);
5126 INT_ON;
5127}
5128
5129/*
5130 * Undo all redirections. Called on error or interrupt.
5131 */
5132
5133/*
5134 * Discard all saved file descriptors.
5135 */
5136static void
5137clearredir(int drop)
5138{
5139 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005140 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005141 if (!redirlist)
5142 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005143 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005144 }
5145}
5146
5147static int
5148redirectsafe(union node *redir, int flags)
5149{
5150 int err;
5151 volatile int saveint;
5152 struct jmploc *volatile savehandler = exception_handler;
5153 struct jmploc jmploc;
5154
5155 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005156 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5157 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005158 if (!err) {
5159 exception_handler = &jmploc;
5160 redirect(redir, flags);
5161 }
5162 exception_handler = savehandler;
5163 if (err && exception != EXERROR)
5164 longjmp(exception_handler->loc, 1);
5165 RESTORE_INT(saveint);
5166 return err;
5167}
5168
5169
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005170/* ============ Routines to expand arguments to commands
5171 *
5172 * We have to deal with backquotes, shell variables, and file metacharacters.
5173 */
5174
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005175#if ENABLE_ASH_MATH_SUPPORT_64
5176typedef int64_t arith_t;
5177#define arith_t_type long long
5178#else
5179typedef long arith_t;
5180#define arith_t_type long
5181#endif
5182
5183#if ENABLE_ASH_MATH_SUPPORT
5184static arith_t dash_arith(const char *);
5185static arith_t arith(const char *expr, int *perrcode);
5186#endif
5187
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005188/*
5189 * expandarg flags
5190 */
5191#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5192#define EXP_TILDE 0x2 /* do normal tilde expansion */
5193#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5194#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5195#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5196#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5197#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5198#define EXP_WORD 0x80 /* expand word in parameter expansion */
5199#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5200/*
5201 * _rmescape() flags
5202 */
5203#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5204#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5205#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5206#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5207#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5208
5209/*
5210 * Structure specifying which parts of the string should be searched
5211 * for IFS characters.
5212 */
5213struct ifsregion {
5214 struct ifsregion *next; /* next region in list */
5215 int begoff; /* offset of start of region */
5216 int endoff; /* offset of end of region */
5217 int nulonly; /* search for nul bytes only */
5218};
5219
5220struct arglist {
5221 struct strlist *list;
5222 struct strlist **lastp;
5223};
5224
5225/* output of current string */
5226static char *expdest;
5227/* list of back quote expressions */
5228static struct nodelist *argbackq;
5229/* first struct in list of ifs regions */
5230static struct ifsregion ifsfirst;
5231/* last struct in list */
5232static struct ifsregion *ifslastp;
5233/* holds expanded arg list */
5234static struct arglist exparg;
5235
5236/*
5237 * Our own itoa().
5238 */
5239static int
5240cvtnum(arith_t num)
5241{
5242 int len;
5243
5244 expdest = makestrspace(32, expdest);
5245#if ENABLE_ASH_MATH_SUPPORT_64
5246 len = fmtstr(expdest, 32, "%lld", (long long) num);
5247#else
5248 len = fmtstr(expdest, 32, "%ld", num);
5249#endif
5250 STADJUST(len, expdest);
5251 return len;
5252}
5253
5254static size_t
5255esclen(const char *start, const char *p)
5256{
5257 size_t esc = 0;
5258
5259 while (p > start && *--p == CTLESC) {
5260 esc++;
5261 }
5262 return esc;
5263}
5264
5265/*
5266 * Remove any CTLESC characters from a string.
5267 */
5268static char *
5269_rmescapes(char *str, int flag)
5270{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005271 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005272
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005273 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005274 unsigned inquotes;
5275 int notescaped;
5276 int globbing;
5277
5278 p = strpbrk(str, qchars);
5279 if (!p) {
5280 return str;
5281 }
5282 q = p;
5283 r = str;
5284 if (flag & RMESCAPE_ALLOC) {
5285 size_t len = p - str;
5286 size_t fulllen = len + strlen(p) + 1;
5287
5288 if (flag & RMESCAPE_GROW) {
5289 r = makestrspace(fulllen, expdest);
5290 } else if (flag & RMESCAPE_HEAP) {
5291 r = ckmalloc(fulllen);
5292 } else {
5293 r = stalloc(fulllen);
5294 }
5295 q = r;
5296 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005297 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005298 }
5299 }
5300 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5301 globbing = flag & RMESCAPE_GLOB;
5302 notescaped = globbing;
5303 while (*p) {
5304 if (*p == CTLQUOTEMARK) {
5305 inquotes = ~inquotes;
5306 p++;
5307 notescaped = globbing;
5308 continue;
5309 }
5310 if (*p == '\\') {
5311 /* naked back slash */
5312 notescaped = 0;
5313 goto copy;
5314 }
5315 if (*p == CTLESC) {
5316 p++;
5317 if (notescaped && inquotes && *p != '/') {
5318 *q++ = '\\';
5319 }
5320 }
5321 notescaped = globbing;
5322 copy:
5323 *q++ = *p++;
5324 }
5325 *q = '\0';
5326 if (flag & RMESCAPE_GROW) {
5327 expdest = r;
5328 STADJUST(q - r + 1, expdest);
5329 }
5330 return r;
5331}
5332#define rmescapes(p) _rmescapes((p), 0)
5333
5334#define pmatch(a, b) !fnmatch((a), (b), 0)
5335
5336/*
5337 * Prepare a pattern for a expmeta (internal glob(3)) call.
5338 *
5339 * Returns an stalloced string.
5340 */
5341static char *
5342preglob(const char *pattern, int quoted, int flag)
5343{
5344 flag |= RMESCAPE_GLOB;
5345 if (quoted) {
5346 flag |= RMESCAPE_QUOTED;
5347 }
5348 return _rmescapes((char *)pattern, flag);
5349}
5350
5351/*
5352 * Put a string on the stack.
5353 */
5354static void
5355memtodest(const char *p, size_t len, int syntax, int quotes)
5356{
5357 char *q = expdest;
5358
5359 q = makestrspace(len * 2, q);
5360
5361 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005362 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005363 if (!c)
5364 continue;
5365 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5366 USTPUTC(CTLESC, q);
5367 USTPUTC(c, q);
5368 }
5369
5370 expdest = q;
5371}
5372
5373static void
5374strtodest(const char *p, int syntax, int quotes)
5375{
5376 memtodest(p, strlen(p), syntax, quotes);
5377}
5378
5379/*
5380 * Record the fact that we have to scan this region of the
5381 * string for IFS characters.
5382 */
5383static void
5384recordregion(int start, int end, int nulonly)
5385{
5386 struct ifsregion *ifsp;
5387
5388 if (ifslastp == NULL) {
5389 ifsp = &ifsfirst;
5390 } else {
5391 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005392 ifsp = ckzalloc(sizeof(*ifsp));
5393 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005394 ifslastp->next = ifsp;
5395 INT_ON;
5396 }
5397 ifslastp = ifsp;
5398 ifslastp->begoff = start;
5399 ifslastp->endoff = end;
5400 ifslastp->nulonly = nulonly;
5401}
5402
5403static void
5404removerecordregions(int endoff)
5405{
5406 if (ifslastp == NULL)
5407 return;
5408
5409 if (ifsfirst.endoff > endoff) {
5410 while (ifsfirst.next != NULL) {
5411 struct ifsregion *ifsp;
5412 INT_OFF;
5413 ifsp = ifsfirst.next->next;
5414 free(ifsfirst.next);
5415 ifsfirst.next = ifsp;
5416 INT_ON;
5417 }
5418 if (ifsfirst.begoff > endoff)
5419 ifslastp = NULL;
5420 else {
5421 ifslastp = &ifsfirst;
5422 ifsfirst.endoff = endoff;
5423 }
5424 return;
5425 }
5426
5427 ifslastp = &ifsfirst;
5428 while (ifslastp->next && ifslastp->next->begoff < endoff)
5429 ifslastp=ifslastp->next;
5430 while (ifslastp->next != NULL) {
5431 struct ifsregion *ifsp;
5432 INT_OFF;
5433 ifsp = ifslastp->next->next;
5434 free(ifslastp->next);
5435 ifslastp->next = ifsp;
5436 INT_ON;
5437 }
5438 if (ifslastp->endoff > endoff)
5439 ifslastp->endoff = endoff;
5440}
5441
5442static char *
5443exptilde(char *startp, char *p, int flag)
5444{
5445 char c;
5446 char *name;
5447 struct passwd *pw;
5448 const char *home;
5449 int quotes = flag & (EXP_FULL | EXP_CASE);
5450 int startloc;
5451
5452 name = p + 1;
5453
5454 while ((c = *++p) != '\0') {
5455 switch (c) {
5456 case CTLESC:
5457 return startp;
5458 case CTLQUOTEMARK:
5459 return startp;
5460 case ':':
5461 if (flag & EXP_VARTILDE)
5462 goto done;
5463 break;
5464 case '/':
5465 case CTLENDVAR:
5466 goto done;
5467 }
5468 }
5469 done:
5470 *p = '\0';
5471 if (*name == '\0') {
5472 home = lookupvar(homestr);
5473 } else {
5474 pw = getpwnam(name);
5475 if (pw == NULL)
5476 goto lose;
5477 home = pw->pw_dir;
5478 }
5479 if (!home || !*home)
5480 goto lose;
5481 *p = c;
5482 startloc = expdest - (char *)stackblock();
5483 strtodest(home, SQSYNTAX, quotes);
5484 recordregion(startloc, expdest - (char *)stackblock(), 0);
5485 return p;
5486 lose:
5487 *p = c;
5488 return startp;
5489}
5490
5491/*
5492 * Execute a command inside back quotes. If it's a builtin command, we
5493 * want to save its output in a block obtained from malloc. Otherwise
5494 * we fork off a subprocess and get the output of the command via a pipe.
5495 * Should be called with interrupts off.
5496 */
5497struct backcmd { /* result of evalbackcmd */
5498 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005499 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005500 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005501 struct job *jp; /* job structure for command */
5502};
5503
5504/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005505static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005506#define EV_EXIT 01 /* exit after evaluating tree */
5507static void evaltree(union node *, int);
5508
5509static void
5510evalbackcmd(union node *n, struct backcmd *result)
5511{
5512 int saveherefd;
5513
5514 result->fd = -1;
5515 result->buf = NULL;
5516 result->nleft = 0;
5517 result->jp = NULL;
5518 if (n == NULL) {
5519 goto out;
5520 }
5521
5522 saveherefd = herefd;
5523 herefd = -1;
5524
5525 {
5526 int pip[2];
5527 struct job *jp;
5528
5529 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005530 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005531 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005532 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5533 FORCE_INT_ON;
5534 close(pip[0]);
5535 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005536 /*close(1);*/
5537 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005538 close(pip[1]);
5539 }
5540 eflag = 0;
5541 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5542 /* NOTREACHED */
5543 }
5544 close(pip[1]);
5545 result->fd = pip[0];
5546 result->jp = jp;
5547 }
5548 herefd = saveherefd;
5549 out:
5550 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5551 result->fd, result->buf, result->nleft, result->jp));
5552}
5553
5554/*
5555 * Expand stuff in backwards quotes.
5556 */
5557static void
5558expbackq(union node *cmd, int quoted, int quotes)
5559{
5560 struct backcmd in;
5561 int i;
5562 char buf[128];
5563 char *p;
5564 char *dest;
5565 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005566 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005567 struct stackmark smark;
5568
5569 INT_OFF;
5570 setstackmark(&smark);
5571 dest = expdest;
5572 startloc = dest - (char *)stackblock();
5573 grabstackstr(dest);
5574 evalbackcmd(cmd, &in);
5575 popstackmark(&smark);
5576
5577 p = in.buf;
5578 i = in.nleft;
5579 if (i == 0)
5580 goto read;
5581 for (;;) {
5582 memtodest(p, i, syntax, quotes);
5583 read:
5584 if (in.fd < 0)
5585 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005586 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005587 TRACE(("expbackq: read returns %d\n", i));
5588 if (i <= 0)
5589 break;
5590 p = buf;
5591 }
5592
Denis Vlasenko60818682007-09-28 22:07:23 +00005593 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005594 if (in.fd >= 0) {
5595 close(in.fd);
5596 back_exitstatus = waitforjob(in.jp);
5597 }
5598 INT_ON;
5599
5600 /* Eat all trailing newlines */
5601 dest = expdest;
5602 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5603 STUNPUTC(dest);
5604 expdest = dest;
5605
5606 if (quoted == 0)
5607 recordregion(startloc, dest - (char *)stackblock(), 0);
5608 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5609 (dest - (char *)stackblock()) - startloc,
5610 (dest - (char *)stackblock()) - startloc,
5611 stackblock() + startloc));
5612}
5613
5614#if ENABLE_ASH_MATH_SUPPORT
5615/*
5616 * Expand arithmetic expression. Backup to start of expression,
5617 * evaluate, place result in (backed up) result, adjust string position.
5618 */
5619static void
5620expari(int quotes)
5621{
5622 char *p, *start;
5623 int begoff;
5624 int flag;
5625 int len;
5626
5627 /* ifsfree(); */
5628
5629 /*
5630 * This routine is slightly over-complicated for
5631 * efficiency. Next we scan backwards looking for the
5632 * start of arithmetic.
5633 */
5634 start = stackblock();
5635 p = expdest - 1;
5636 *p = '\0';
5637 p--;
5638 do {
5639 int esc;
5640
5641 while (*p != CTLARI) {
5642 p--;
5643#if DEBUG
5644 if (p < start) {
5645 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5646 }
5647#endif
5648 }
5649
5650 esc = esclen(start, p);
5651 if (!(esc % 2)) {
5652 break;
5653 }
5654
5655 p -= esc + 1;
5656 } while (1);
5657
5658 begoff = p - start;
5659
5660 removerecordregions(begoff);
5661
5662 flag = p[1];
5663
5664 expdest = p;
5665
5666 if (quotes)
5667 rmescapes(p + 2);
5668
5669 len = cvtnum(dash_arith(p + 2));
5670
5671 if (flag != '"')
5672 recordregion(begoff, begoff + len, 0);
5673}
5674#endif
5675
5676/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005677static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005678
5679/*
5680 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5681 * characters to allow for further processing. Otherwise treat
5682 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005683 *
5684 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5685 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5686 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005687 */
5688static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005689argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005690{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005691 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005692 '=',
5693 ':',
5694 CTLQUOTEMARK,
5695 CTLENDVAR,
5696 CTLESC,
5697 CTLVAR,
5698 CTLBACKQ,
5699 CTLBACKQ | CTLQUOTE,
5700#if ENABLE_ASH_MATH_SUPPORT
5701 CTLENDARI,
5702#endif
5703 0
5704 };
5705 const char *reject = spclchars;
5706 int c;
5707 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5708 int breakall = flag & EXP_WORD;
5709 int inquotes;
5710 size_t length;
5711 int startloc;
5712
5713 if (!(flag & EXP_VARTILDE)) {
5714 reject += 2;
5715 } else if (flag & EXP_VARTILDE2) {
5716 reject++;
5717 }
5718 inquotes = 0;
5719 length = 0;
5720 if (flag & EXP_TILDE) {
5721 char *q;
5722
5723 flag &= ~EXP_TILDE;
5724 tilde:
5725 q = p;
5726 if (*q == CTLESC && (flag & EXP_QWORD))
5727 q++;
5728 if (*q == '~')
5729 p = exptilde(p, q, flag);
5730 }
5731 start:
5732 startloc = expdest - (char *)stackblock();
5733 for (;;) {
5734 length += strcspn(p + length, reject);
5735 c = p[length];
5736 if (c && (!(c & 0x80)
5737#if ENABLE_ASH_MATH_SUPPORT
5738 || c == CTLENDARI
5739#endif
5740 )) {
5741 /* c == '=' || c == ':' || c == CTLENDARI */
5742 length++;
5743 }
5744 if (length > 0) {
5745 int newloc;
5746 expdest = stack_nputstr(p, length, expdest);
5747 newloc = expdest - (char *)stackblock();
5748 if (breakall && !inquotes && newloc > startloc) {
5749 recordregion(startloc, newloc, 0);
5750 }
5751 startloc = newloc;
5752 }
5753 p += length + 1;
5754 length = 0;
5755
5756 switch (c) {
5757 case '\0':
5758 goto breakloop;
5759 case '=':
5760 if (flag & EXP_VARTILDE2) {
5761 p--;
5762 continue;
5763 }
5764 flag |= EXP_VARTILDE2;
5765 reject++;
5766 /* fall through */
5767 case ':':
5768 /*
5769 * sort of a hack - expand tildes in variable
5770 * assignments (after the first '=' and after ':'s).
5771 */
5772 if (*--p == '~') {
5773 goto tilde;
5774 }
5775 continue;
5776 }
5777
5778 switch (c) {
5779 case CTLENDVAR: /* ??? */
5780 goto breakloop;
5781 case CTLQUOTEMARK:
5782 /* "$@" syntax adherence hack */
5783 if (
5784 !inquotes &&
5785 !memcmp(p, dolatstr, 4) &&
5786 (p[4] == CTLQUOTEMARK || (
5787 p[4] == CTLENDVAR &&
5788 p[5] == CTLQUOTEMARK
5789 ))
5790 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005791 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005792 goto start;
5793 }
5794 inquotes = !inquotes;
5795 addquote:
5796 if (quotes) {
5797 p--;
5798 length++;
5799 startloc++;
5800 }
5801 break;
5802 case CTLESC:
5803 startloc++;
5804 length++;
5805 goto addquote;
5806 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005807 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005808 goto start;
5809 case CTLBACKQ:
5810 c = 0;
5811 case CTLBACKQ|CTLQUOTE:
5812 expbackq(argbackq->n, c, quotes);
5813 argbackq = argbackq->next;
5814 goto start;
5815#if ENABLE_ASH_MATH_SUPPORT
5816 case CTLENDARI:
5817 p--;
5818 expari(quotes);
5819 goto start;
5820#endif
5821 }
5822 }
5823 breakloop:
5824 ;
5825}
5826
5827static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005828scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005829 int zero)
5830{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005831// This commented out code was added by James Simmons <jsimmons@infradead.org>
5832// as part of a larger change when he added support for ${var/a/b}.
5833// However, it broke # and % operators:
5834//
5835//var=ababcdcd
5836// ok bad
5837//echo ${var#ab} abcdcd abcdcd
5838//echo ${var##ab} abcdcd abcdcd
5839//echo ${var#a*b} abcdcd ababcdcd (!)
5840//echo ${var##a*b} cdcd cdcd
5841//echo ${var#?} babcdcd ababcdcd (!)
5842//echo ${var##?} babcdcd babcdcd
5843//echo ${var#*} ababcdcd babcdcd (!)
5844//echo ${var##*}
5845//echo ${var%cd} ababcd ababcd
5846//echo ${var%%cd} ababcd abab (!)
5847//echo ${var%c*d} ababcd ababcd
5848//echo ${var%%c*d} abab ababcdcd (!)
5849//echo ${var%?} ababcdc ababcdc
5850//echo ${var%%?} ababcdc ababcdcd (!)
5851//echo ${var%*} ababcdcd ababcdcd
5852//echo ${var%%*}
5853//
5854// Commenting it back out helped. Remove it completely if it really
5855// is not needed.
5856
5857 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005858 char c;
5859
5860 loc = startp;
5861 loc2 = rmesc;
5862 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005863 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005864 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005865
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 c = *loc2;
5867 if (zero) {
5868 *loc2 = '\0';
5869 s = rmesc;
5870 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005871 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005872
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005873// // chop off end if its '*'
5874// full = strrchr(str, '*');
5875// if (full && full != str)
5876// match--;
5877//
5878// // If str starts with '*' replace with s.
5879// if ((*str == '*') && strlen(s) >= match) {
5880// full = xstrdup(s);
5881// strncpy(full+strlen(s)-match+1, str+1, match-1);
5882// } else
5883// full = xstrndup(str, match);
5884// match = strncmp(s, full, strlen(full));
5885// free(full);
5886//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005887 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005888 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005889 return loc;
5890 if (quotes && *loc == CTLESC)
5891 loc++;
5892 loc++;
5893 loc2++;
5894 } while (c);
5895 return 0;
5896}
5897
5898static char *
5899scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5900 int zero)
5901{
5902 int esc = 0;
5903 char *loc;
5904 char *loc2;
5905
5906 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5907 int match;
5908 char c = *loc2;
5909 const char *s = loc2;
5910 if (zero) {
5911 *loc2 = '\0';
5912 s = rmesc;
5913 }
5914 match = pmatch(str, s);
5915 *loc2 = c;
5916 if (match)
5917 return loc;
5918 loc--;
5919 if (quotes) {
5920 if (--esc < 0) {
5921 esc = esclen(startp, loc);
5922 }
5923 if (esc % 2) {
5924 esc--;
5925 loc--;
5926 }
5927 }
5928 }
5929 return 0;
5930}
5931
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005932static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005933static void
5934varunset(const char *end, const char *var, const char *umsg, int varflags)
5935{
5936 const char *msg;
5937 const char *tail;
5938
5939 tail = nullstr;
5940 msg = "parameter not set";
5941 if (umsg) {
5942 if (*end == CTLENDVAR) {
5943 if (varflags & VSNUL)
5944 tail = " or null";
5945 } else
5946 msg = umsg;
5947 }
5948 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5949}
5950
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005951#if ENABLE_ASH_BASH_COMPAT
5952static char *
5953parse_sub_pattern(char *arg, int inquotes)
5954{
5955 char *idx, *repl = NULL;
5956 unsigned char c;
5957
Denis Vlasenko2659c632008-06-14 06:04:59 +00005958 idx = arg;
5959 while (1) {
5960 c = *arg;
5961 if (!c)
5962 break;
5963 if (c == '/') {
5964 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005965 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005966 repl = idx + 1;
5967 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005968 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005969 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005970 *idx++ = c;
5971 if (!inquotes && c == '\\' && arg[1] == '\\')
5972 arg++; /* skip both \\, not just first one */
5973 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005974 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005975 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005976
5977 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005978}
5979#endif /* ENABLE_ASH_BASH_COMPAT */
5980
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005981static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005982subevalvar(char *p, char *str, int strloc, int subtype,
5983 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005984{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005985 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005986 char *startp;
5987 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005988 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005989 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5990 USE_ASH_BASH_COMPAT(char null = '\0';)
5991 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5992 int saveherefd = herefd;
5993 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005994 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005995 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996
5997 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005998 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5999 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006000 STPUTC('\0', expdest);
6001 herefd = saveherefd;
6002 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006003 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006004
6005 switch (subtype) {
6006 case VSASSIGN:
6007 setvar(str, startp, 0);
6008 amount = startp - expdest;
6009 STADJUST(amount, expdest);
6010 return startp;
6011
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006012#if ENABLE_ASH_BASH_COMPAT
6013 case VSSUBSTR:
6014 loc = str = stackblock() + strloc;
6015// TODO: number() instead? It does error checking...
6016 pos = atoi(loc);
6017 len = str - startp - 1;
6018
6019 /* *loc != '\0', guaranteed by parser */
6020 if (quotes) {
6021 char *ptr;
6022
6023 /* We must adjust the length by the number of escapes we find. */
6024 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006025 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006026 len--;
6027 ptr++;
6028 }
6029 }
6030 }
6031 orig_len = len;
6032
6033 if (*loc++ == ':') {
6034// TODO: number() instead? It does error checking...
6035 len = atoi(loc);
6036 } else {
6037 len = orig_len;
6038 while (*loc && *loc != ':')
6039 loc++;
6040 if (*loc++ == ':')
6041// TODO: number() instead? It does error checking...
6042 len = atoi(loc);
6043 }
6044 if (pos >= orig_len) {
6045 pos = 0;
6046 len = 0;
6047 }
6048 if (len > (orig_len - pos))
6049 len = orig_len - pos;
6050
6051 for (str = startp; pos; str++, pos--) {
6052 if (quotes && *str == CTLESC)
6053 str++;
6054 }
6055 for (loc = startp; len; len--) {
6056 if (quotes && *str == CTLESC)
6057 *loc++ = *str++;
6058 *loc++ = *str++;
6059 }
6060 *loc = '\0';
6061 amount = loc - expdest;
6062 STADJUST(amount, expdest);
6063 return loc;
6064#endif
6065
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006066 case VSQUESTION:
6067 varunset(p, str, startp, varflags);
6068 /* NOTREACHED */
6069 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006070 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006071
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006072 /* We'll comeback here if we grow the stack while handling
6073 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6074 * stack will need rebasing, and we'll need to remove our work
6075 * areas each time
6076 */
6077 USE_ASH_BASH_COMPAT(restart:)
6078
6079 amount = expdest - ((char *)stackblock() + resetloc);
6080 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006081 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006082
6083 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006084 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006085 if (quotes) {
6086 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6087 if (rmesc != startp) {
6088 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006089 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090 }
6091 }
6092 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006093 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006094 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006095 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006096
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006097#if ENABLE_ASH_BASH_COMPAT
6098 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6099 char *idx, *end, *restart_detect;
6100
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006101 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006102 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6103 if (!repl)
6104 repl = &null;
6105 }
6106
6107 /* If there's no pattern to match, return the expansion unmolested */
6108 if (*str == '\0')
6109 return 0;
6110
6111 len = 0;
6112 idx = startp;
6113 end = str - 1;
6114 while (idx < end) {
6115 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6116 if (!loc) {
6117 /* No match, advance */
6118 restart_detect = stackblock();
6119 STPUTC(*idx, expdest);
6120 if (quotes && *idx == CTLESC) {
6121 idx++;
6122 len++;
6123 STPUTC(*idx, expdest);
6124 }
6125 if (stackblock() != restart_detect)
6126 goto restart;
6127 idx++;
6128 len++;
6129 rmesc++;
6130 continue;
6131 }
6132
6133 if (subtype == VSREPLACEALL) {
6134 while (idx < loc) {
6135 if (quotes && *idx == CTLESC)
6136 idx++;
6137 idx++;
6138 rmesc++;
6139 }
6140 } else
6141 idx = loc;
6142
6143 for (loc = repl; *loc; loc++) {
6144 restart_detect = stackblock();
6145 STPUTC(*loc, expdest);
6146 if (stackblock() != restart_detect)
6147 goto restart;
6148 len++;
6149 }
6150
6151 if (subtype == VSREPLACE) {
6152 while (*idx) {
6153 restart_detect = stackblock();
6154 STPUTC(*idx, expdest);
6155 if (stackblock() != restart_detect)
6156 goto restart;
6157 len++;
6158 idx++;
6159 }
6160 break;
6161 }
6162 }
6163
6164 /* We've put the replaced text into a buffer at workloc, now
6165 * move it to the right place and adjust the stack.
6166 */
6167 startp = stackblock() + startloc;
6168 STPUTC('\0', expdest);
6169 memmove(startp, stackblock() + workloc, len);
6170 startp[len++] = '\0';
6171 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6172 STADJUST(-amount, expdest);
6173 return startp;
6174 }
6175#endif /* ENABLE_ASH_BASH_COMPAT */
6176
6177 subtype -= VSTRIMRIGHT;
6178#if DEBUG
6179 if (subtype < 0 || subtype > 7)
6180 abort();
6181#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6183 zero = subtype >> 1;
6184 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6185 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6186
6187 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6188 if (loc) {
6189 if (zero) {
6190 memmove(startp, loc, str - loc);
6191 loc = startp + (str - loc) - 1;
6192 }
6193 *loc = '\0';
6194 amount = loc - expdest;
6195 STADJUST(amount, expdest);
6196 }
6197 return loc;
6198}
6199
6200/*
6201 * Add the value of a specialized variable to the stack string.
6202 */
6203static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006204varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006205{
6206 int num;
6207 char *p;
6208 int i;
6209 int sep = 0;
6210 int sepq = 0;
6211 ssize_t len = 0;
6212 char **ap;
6213 int syntax;
6214 int quoted = varflags & VSQUOTE;
6215 int subtype = varflags & VSTYPE;
6216 int quotes = flags & (EXP_FULL | EXP_CASE);
6217
6218 if (quoted && (flags & EXP_FULL))
6219 sep = 1 << CHAR_BIT;
6220
6221 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6222 switch (*name) {
6223 case '$':
6224 num = rootpid;
6225 goto numvar;
6226 case '?':
6227 num = exitstatus;
6228 goto numvar;
6229 case '#':
6230 num = shellparam.nparam;
6231 goto numvar;
6232 case '!':
6233 num = backgndpid;
6234 if (num == 0)
6235 return -1;
6236 numvar:
6237 len = cvtnum(num);
6238 break;
6239 case '-':
6240 p = makestrspace(NOPTS, expdest);
6241 for (i = NOPTS - 1; i >= 0; i--) {
6242 if (optlist[i]) {
6243 USTPUTC(optletters(i), p);
6244 len++;
6245 }
6246 }
6247 expdest = p;
6248 break;
6249 case '@':
6250 if (sep)
6251 goto param;
6252 /* fall through */
6253 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006254 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006255 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6256 sepq = 1;
6257 param:
6258 ap = shellparam.p;
6259 if (!ap)
6260 return -1;
6261 while ((p = *ap++)) {
6262 size_t partlen;
6263
6264 partlen = strlen(p);
6265 len += partlen;
6266
6267 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6268 memtodest(p, partlen, syntax, quotes);
6269
6270 if (*ap && sep) {
6271 char *q;
6272
6273 len++;
6274 if (subtype == VSPLUS || subtype == VSLENGTH) {
6275 continue;
6276 }
6277 q = expdest;
6278 if (sepq)
6279 STPUTC(CTLESC, q);
6280 STPUTC(sep, q);
6281 expdest = q;
6282 }
6283 }
6284 return len;
6285 case '0':
6286 case '1':
6287 case '2':
6288 case '3':
6289 case '4':
6290 case '5':
6291 case '6':
6292 case '7':
6293 case '8':
6294 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006295// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296 num = atoi(name);
6297 if (num < 0 || num > shellparam.nparam)
6298 return -1;
6299 p = num ? shellparam.p[num - 1] : arg0;
6300 goto value;
6301 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006302 /* NB: name has form "VAR=..." */
6303
6304 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6305 * which should be considered before we check variables. */
6306 if (var_str_list) {
6307 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6308 p = NULL;
6309 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006310 char *str, *eq;
6311 str = var_str_list->text;
6312 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006313 if (!eq) /* stop at first non-assignment */
6314 break;
6315 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006316 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006317 && strncmp(str, name, name_len) == 0) {
6318 p = eq;
6319 /* goto value; - WRONG! */
6320 /* think "A=1 A=2 B=$A" */
6321 }
6322 var_str_list = var_str_list->next;
6323 } while (var_str_list);
6324 if (p)
6325 goto value;
6326 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006327 p = lookupvar(name);
6328 value:
6329 if (!p)
6330 return -1;
6331
6332 len = strlen(p);
6333 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6334 memtodest(p, len, syntax, quotes);
6335 return len;
6336 }
6337
6338 if (subtype == VSPLUS || subtype == VSLENGTH)
6339 STADJUST(-len, expdest);
6340 return len;
6341}
6342
6343/*
6344 * Expand a variable, and return a pointer to the next character in the
6345 * input string.
6346 */
6347static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006348evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006349{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006350 char varflags;
6351 char subtype;
6352 char quoted;
6353 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006354 char *var;
6355 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356 int startloc;
6357 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006358
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006359 varflags = *p++;
6360 subtype = varflags & VSTYPE;
6361 quoted = varflags & VSQUOTE;
6362 var = p;
6363 easy = (!quoted || (*var == '@' && shellparam.nparam));
6364 startloc = expdest - (char *)stackblock();
6365 p = strchr(p, '=') + 1;
6366
6367 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006368 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006369 if (varflags & VSNUL)
6370 varlen--;
6371
6372 if (subtype == VSPLUS) {
6373 varlen = -1 - varlen;
6374 goto vsplus;
6375 }
6376
6377 if (subtype == VSMINUS) {
6378 vsplus:
6379 if (varlen < 0) {
6380 argstr(
6381 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006382 (quoted ? EXP_QWORD : EXP_WORD),
6383 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006384 );
6385 goto end;
6386 }
6387 if (easy)
6388 goto record;
6389 goto end;
6390 }
6391
6392 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6393 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006394 if (subevalvar(p, var, /* strloc: */ 0,
6395 subtype, startloc, varflags,
6396 /* quotes: */ 0,
6397 var_str_list)
6398 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006399 varflags &= ~VSNUL;
6400 /*
6401 * Remove any recorded regions beyond
6402 * start of variable
6403 */
6404 removerecordregions(startloc);
6405 goto again;
6406 }
6407 goto end;
6408 }
6409 if (easy)
6410 goto record;
6411 goto end;
6412 }
6413
6414 if (varlen < 0 && uflag)
6415 varunset(p, var, 0, 0);
6416
6417 if (subtype == VSLENGTH) {
6418 cvtnum(varlen > 0 ? varlen : 0);
6419 goto record;
6420 }
6421
6422 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006423 if (easy)
6424 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006425 goto end;
6426 }
6427
6428#if DEBUG
6429 switch (subtype) {
6430 case VSTRIMLEFT:
6431 case VSTRIMLEFTMAX:
6432 case VSTRIMRIGHT:
6433 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006434#if ENABLE_ASH_BASH_COMPAT
6435 case VSSUBSTR:
6436 case VSREPLACE:
6437 case VSREPLACEALL:
6438#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006439 break;
6440 default:
6441 abort();
6442 }
6443#endif
6444
6445 if (varlen >= 0) {
6446 /*
6447 * Terminate the string and start recording the pattern
6448 * right after it
6449 */
6450 STPUTC('\0', expdest);
6451 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006452 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6453 startloc, varflags,
6454 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6455 var_str_list)
6456 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006457 int amount = expdest - (
6458 (char *)stackblock() + patloc - 1
6459 );
6460 STADJUST(-amount, expdest);
6461 }
6462 /* Remove any recorded regions beyond start of variable */
6463 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006464 record:
6465 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006466 }
6467
6468 end:
6469 if (subtype != VSNORMAL) { /* skip to end of alternative */
6470 int nesting = 1;
6471 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006472 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006473 if (c == CTLESC)
6474 p++;
6475 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6476 if (varlen >= 0)
6477 argbackq = argbackq->next;
6478 } else if (c == CTLVAR) {
6479 if ((*p++ & VSTYPE) != VSNORMAL)
6480 nesting++;
6481 } else if (c == CTLENDVAR) {
6482 if (--nesting == 0)
6483 break;
6484 }
6485 }
6486 }
6487 return p;
6488}
6489
6490/*
6491 * Break the argument string into pieces based upon IFS and add the
6492 * strings to the argument list. The regions of the string to be
6493 * searched for IFS characters have been stored by recordregion.
6494 */
6495static void
6496ifsbreakup(char *string, struct arglist *arglist)
6497{
6498 struct ifsregion *ifsp;
6499 struct strlist *sp;
6500 char *start;
6501 char *p;
6502 char *q;
6503 const char *ifs, *realifs;
6504 int ifsspc;
6505 int nulonly;
6506
6507 start = string;
6508 if (ifslastp != NULL) {
6509 ifsspc = 0;
6510 nulonly = 0;
6511 realifs = ifsset() ? ifsval() : defifs;
6512 ifsp = &ifsfirst;
6513 do {
6514 p = string + ifsp->begoff;
6515 nulonly = ifsp->nulonly;
6516 ifs = nulonly ? nullstr : realifs;
6517 ifsspc = 0;
6518 while (p < string + ifsp->endoff) {
6519 q = p;
6520 if (*p == CTLESC)
6521 p++;
6522 if (!strchr(ifs, *p)) {
6523 p++;
6524 continue;
6525 }
6526 if (!nulonly)
6527 ifsspc = (strchr(defifs, *p) != NULL);
6528 /* Ignore IFS whitespace at start */
6529 if (q == start && ifsspc) {
6530 p++;
6531 start = p;
6532 continue;
6533 }
6534 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006535 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006536 sp->text = start;
6537 *arglist->lastp = sp;
6538 arglist->lastp = &sp->next;
6539 p++;
6540 if (!nulonly) {
6541 for (;;) {
6542 if (p >= string + ifsp->endoff) {
6543 break;
6544 }
6545 q = p;
6546 if (*p == CTLESC)
6547 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006548 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006549 p = q;
6550 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006551 }
6552 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006553 if (ifsspc) {
6554 p++;
6555 ifsspc = 0;
6556 } else {
6557 p = q;
6558 break;
6559 }
6560 } else
6561 p++;
6562 }
6563 }
6564 start = p;
6565 } /* while */
6566 ifsp = ifsp->next;
6567 } while (ifsp != NULL);
6568 if (nulonly)
6569 goto add;
6570 }
6571
6572 if (!*start)
6573 return;
6574
6575 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006576 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006577 sp->text = start;
6578 *arglist->lastp = sp;
6579 arglist->lastp = &sp->next;
6580}
6581
6582static void
6583ifsfree(void)
6584{
6585 struct ifsregion *p;
6586
6587 INT_OFF;
6588 p = ifsfirst.next;
6589 do {
6590 struct ifsregion *ifsp;
6591 ifsp = p->next;
6592 free(p);
6593 p = ifsp;
6594 } while (p);
6595 ifslastp = NULL;
6596 ifsfirst.next = NULL;
6597 INT_ON;
6598}
6599
6600/*
6601 * Add a file name to the list.
6602 */
6603static void
6604addfname(const char *name)
6605{
6606 struct strlist *sp;
6607
Denis Vlasenko597906c2008-02-20 16:38:54 +00006608 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006609 sp->text = ststrdup(name);
6610 *exparg.lastp = sp;
6611 exparg.lastp = &sp->next;
6612}
6613
6614static char *expdir;
6615
6616/*
6617 * Do metacharacter (i.e. *, ?, [...]) expansion.
6618 */
6619static void
6620expmeta(char *enddir, char *name)
6621{
6622 char *p;
6623 const char *cp;
6624 char *start;
6625 char *endname;
6626 int metaflag;
6627 struct stat statb;
6628 DIR *dirp;
6629 struct dirent *dp;
6630 int atend;
6631 int matchdot;
6632
6633 metaflag = 0;
6634 start = name;
6635 for (p = name; *p; p++) {
6636 if (*p == '*' || *p == '?')
6637 metaflag = 1;
6638 else if (*p == '[') {
6639 char *q = p + 1;
6640 if (*q == '!')
6641 q++;
6642 for (;;) {
6643 if (*q == '\\')
6644 q++;
6645 if (*q == '/' || *q == '\0')
6646 break;
6647 if (*++q == ']') {
6648 metaflag = 1;
6649 break;
6650 }
6651 }
6652 } else if (*p == '\\')
6653 p++;
6654 else if (*p == '/') {
6655 if (metaflag)
6656 goto out;
6657 start = p + 1;
6658 }
6659 }
6660 out:
6661 if (metaflag == 0) { /* we've reached the end of the file name */
6662 if (enddir != expdir)
6663 metaflag++;
6664 p = name;
6665 do {
6666 if (*p == '\\')
6667 p++;
6668 *enddir++ = *p;
6669 } while (*p++);
6670 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6671 addfname(expdir);
6672 return;
6673 }
6674 endname = p;
6675 if (name < start) {
6676 p = name;
6677 do {
6678 if (*p == '\\')
6679 p++;
6680 *enddir++ = *p++;
6681 } while (p < start);
6682 }
6683 if (enddir == expdir) {
6684 cp = ".";
6685 } else if (enddir == expdir + 1 && *expdir == '/') {
6686 cp = "/";
6687 } else {
6688 cp = expdir;
6689 enddir[-1] = '\0';
6690 }
6691 dirp = opendir(cp);
6692 if (dirp == NULL)
6693 return;
6694 if (enddir != expdir)
6695 enddir[-1] = '/';
6696 if (*endname == 0) {
6697 atend = 1;
6698 } else {
6699 atend = 0;
6700 *endname++ = '\0';
6701 }
6702 matchdot = 0;
6703 p = start;
6704 if (*p == '\\')
6705 p++;
6706 if (*p == '.')
6707 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006708 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006709 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006710 continue;
6711 if (pmatch(start, dp->d_name)) {
6712 if (atend) {
6713 strcpy(enddir, dp->d_name);
6714 addfname(expdir);
6715 } else {
6716 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6717 continue;
6718 p[-1] = '/';
6719 expmeta(p, endname);
6720 }
6721 }
6722 }
6723 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006724 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006725 endname[-1] = '/';
6726}
6727
6728static struct strlist *
6729msort(struct strlist *list, int len)
6730{
6731 struct strlist *p, *q = NULL;
6732 struct strlist **lpp;
6733 int half;
6734 int n;
6735
6736 if (len <= 1)
6737 return list;
6738 half = len >> 1;
6739 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006740 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006741 q = p;
6742 p = p->next;
6743 }
6744 q->next = NULL; /* terminate first half of list */
6745 q = msort(list, half); /* sort first half of list */
6746 p = msort(p, len - half); /* sort second half */
6747 lpp = &list;
6748 for (;;) {
6749#if ENABLE_LOCALE_SUPPORT
6750 if (strcoll(p->text, q->text) < 0)
6751#else
6752 if (strcmp(p->text, q->text) < 0)
6753#endif
6754 {
6755 *lpp = p;
6756 lpp = &p->next;
6757 p = *lpp;
6758 if (p == NULL) {
6759 *lpp = q;
6760 break;
6761 }
6762 } else {
6763 *lpp = q;
6764 lpp = &q->next;
6765 q = *lpp;
6766 if (q == NULL) {
6767 *lpp = p;
6768 break;
6769 }
6770 }
6771 }
6772 return list;
6773}
6774
6775/*
6776 * Sort the results of file name expansion. It calculates the number of
6777 * strings to sort and then calls msort (short for merge sort) to do the
6778 * work.
6779 */
6780static struct strlist *
6781expsort(struct strlist *str)
6782{
6783 int len;
6784 struct strlist *sp;
6785
6786 len = 0;
6787 for (sp = str; sp; sp = sp->next)
6788 len++;
6789 return msort(str, len);
6790}
6791
6792static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006793expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006794{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006795 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006796 '*', '?', '[', 0
6797 };
6798 /* TODO - EXP_REDIR */
6799
6800 while (str) {
6801 struct strlist **savelastp;
6802 struct strlist *sp;
6803 char *p;
6804
6805 if (fflag)
6806 goto nometa;
6807 if (!strpbrk(str->text, metachars))
6808 goto nometa;
6809 savelastp = exparg.lastp;
6810
6811 INT_OFF;
6812 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6813 {
6814 int i = strlen(str->text);
6815 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6816 }
6817
6818 expmeta(expdir, p);
6819 free(expdir);
6820 if (p != str->text)
6821 free(p);
6822 INT_ON;
6823 if (exparg.lastp == savelastp) {
6824 /*
6825 * no matches
6826 */
6827 nometa:
6828 *exparg.lastp = str;
6829 rmescapes(str->text);
6830 exparg.lastp = &str->next;
6831 } else {
6832 *exparg.lastp = NULL;
6833 *savelastp = sp = expsort(*savelastp);
6834 while (sp->next != NULL)
6835 sp = sp->next;
6836 exparg.lastp = &sp->next;
6837 }
6838 str = str->next;
6839 }
6840}
6841
6842/*
6843 * Perform variable substitution and command substitution on an argument,
6844 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6845 * perform splitting and file name expansion. When arglist is NULL, perform
6846 * here document expansion.
6847 */
6848static void
6849expandarg(union node *arg, struct arglist *arglist, int flag)
6850{
6851 struct strlist *sp;
6852 char *p;
6853
6854 argbackq = arg->narg.backquote;
6855 STARTSTACKSTR(expdest);
6856 ifsfirst.next = NULL;
6857 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006858 argstr(arg->narg.text, flag,
6859 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006860 p = _STPUTC('\0', expdest);
6861 expdest = p - 1;
6862 if (arglist == NULL) {
6863 return; /* here document expanded */
6864 }
6865 p = grabstackstr(p);
6866 exparg.lastp = &exparg.list;
6867 /*
6868 * TODO - EXP_REDIR
6869 */
6870 if (flag & EXP_FULL) {
6871 ifsbreakup(p, &exparg);
6872 *exparg.lastp = NULL;
6873 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006874 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006875 } else {
6876 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6877 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006878 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006879 sp->text = p;
6880 *exparg.lastp = sp;
6881 exparg.lastp = &sp->next;
6882 }
6883 if (ifsfirst.next)
6884 ifsfree();
6885 *exparg.lastp = NULL;
6886 if (exparg.list) {
6887 *arglist->lastp = exparg.list;
6888 arglist->lastp = exparg.lastp;
6889 }
6890}
6891
6892/*
6893 * Expand shell variables and backquotes inside a here document.
6894 */
6895static void
6896expandhere(union node *arg, int fd)
6897{
6898 herefd = fd;
6899 expandarg(arg, (struct arglist *)NULL, 0);
6900 full_write(fd, stackblock(), expdest - (char *)stackblock());
6901}
6902
6903/*
6904 * Returns true if the pattern matches the string.
6905 */
6906static int
6907patmatch(char *pattern, const char *string)
6908{
6909 return pmatch(preglob(pattern, 0, 0), string);
6910}
6911
6912/*
6913 * See if a pattern matches in a case statement.
6914 */
6915static int
6916casematch(union node *pattern, char *val)
6917{
6918 struct stackmark smark;
6919 int result;
6920
6921 setstackmark(&smark);
6922 argbackq = pattern->narg.backquote;
6923 STARTSTACKSTR(expdest);
6924 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006925 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6926 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006927 STACKSTRNUL(expdest);
6928 result = patmatch(stackblock(), val);
6929 popstackmark(&smark);
6930 return result;
6931}
6932
6933
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006934/* ============ find_command */
6935
6936struct builtincmd {
6937 const char *name;
6938 int (*builtin)(int, char **);
6939 /* unsigned flags; */
6940};
6941#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006942/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006943 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006944#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006945#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006946
6947struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006948 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006949 union param {
6950 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006951 /* index >= 0 for commands without path (slashes) */
6952 /* (TODO: what exactly does the value mean? PATH position?) */
6953 /* index == -1 for commands with slashes */
6954 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006955 const struct builtincmd *cmd;
6956 struct funcnode *func;
6957 } u;
6958};
6959/* values of cmdtype */
6960#define CMDUNKNOWN -1 /* no entry in table for command */
6961#define CMDNORMAL 0 /* command is an executable program */
6962#define CMDFUNCTION 1 /* command is a shell function */
6963#define CMDBUILTIN 2 /* command is a shell builtin */
6964
6965/* action to find_command() */
6966#define DO_ERR 0x01 /* prints errors */
6967#define DO_ABS 0x02 /* checks absolute paths */
6968#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6969#define DO_ALTPATH 0x08 /* using alternate path */
6970#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6971
6972static void find_command(char *, struct cmdentry *, int, const char *);
6973
6974
6975/* ============ Hashing commands */
6976
6977/*
6978 * When commands are first encountered, they are entered in a hash table.
6979 * This ensures that a full path search will not have to be done for them
6980 * on each invocation.
6981 *
6982 * We should investigate converting to a linear search, even though that
6983 * would make the command name "hash" a misnomer.
6984 */
6985
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006986struct tblentry {
6987 struct tblentry *next; /* next entry in hash chain */
6988 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006989 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006990 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006991 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006992};
6993
Denis Vlasenko01631112007-12-16 17:20:38 +00006994static struct tblentry **cmdtable;
6995#define INIT_G_cmdtable() do { \
6996 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6997} while (0)
6998
6999static int builtinloc = -1; /* index in path of %builtin, or -1 */
7000
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007001
7002static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007003tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007004{
7005 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007006
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007007#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007008 if (applet_no >= 0) {
7009 if (APPLET_IS_NOEXEC(applet_no))
7010 run_applet_no_and_exit(applet_no, argv);
7011 /* re-exec ourselves with the new arguments */
7012 execve(bb_busybox_exec_path, argv, envp);
7013 /* If they called chroot or otherwise made the binary no longer
7014 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007015 }
7016#endif
7017
7018 repeat:
7019#ifdef SYSV
7020 do {
7021 execve(cmd, argv, envp);
7022 } while (errno == EINTR);
7023#else
7024 execve(cmd, argv, envp);
7025#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007026 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007027 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007028 return;
7029 }
7030 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007031 char **ap;
7032 char **new;
7033
7034 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007035 continue;
7036 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007037 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007038 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007039 ap += 2;
7040 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007041 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007042 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007043 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007044 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007045 goto repeat;
7046 }
7047}
7048
7049/*
7050 * Exec a program. Never returns. If you change this routine, you may
7051 * have to change the find_command routine as well.
7052 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007053static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007054static void
7055shellexec(char **argv, const char *path, int idx)
7056{
7057 char *cmdname;
7058 int e;
7059 char **envp;
7060 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007061#if ENABLE_FEATURE_SH_STANDALONE
7062 int applet_no = -1;
7063#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007064
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007065 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007066 envp = listvars(VEXPORT, VUNSET, 0);
7067 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007068#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007069 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007070#endif
7071 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007072 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007073 e = errno;
7074 } else {
7075 e = ENOENT;
7076 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7077 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007078 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007079 if (errno != ENOENT && errno != ENOTDIR)
7080 e = errno;
7081 }
7082 stunalloc(cmdname);
7083 }
7084 }
7085
7086 /* Map to POSIX errors */
7087 switch (e) {
7088 case EACCES:
7089 exerrno = 126;
7090 break;
7091 case ENOENT:
7092 exerrno = 127;
7093 break;
7094 default:
7095 exerrno = 2;
7096 break;
7097 }
7098 exitstatus = exerrno;
7099 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007100 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007101 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7102 /* NOTREACHED */
7103}
7104
7105static void
7106printentry(struct tblentry *cmdp)
7107{
7108 int idx;
7109 const char *path;
7110 char *name;
7111
7112 idx = cmdp->param.index;
7113 path = pathval();
7114 do {
7115 name = padvance(&path, cmdp->cmdname);
7116 stunalloc(name);
7117 } while (--idx >= 0);
7118 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7119}
7120
7121/*
7122 * Clear out command entries. The argument specifies the first entry in
7123 * PATH which has changed.
7124 */
7125static void
7126clearcmdentry(int firstchange)
7127{
7128 struct tblentry **tblp;
7129 struct tblentry **pp;
7130 struct tblentry *cmdp;
7131
7132 INT_OFF;
7133 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7134 pp = tblp;
7135 while ((cmdp = *pp) != NULL) {
7136 if ((cmdp->cmdtype == CMDNORMAL &&
7137 cmdp->param.index >= firstchange)
7138 || (cmdp->cmdtype == CMDBUILTIN &&
7139 builtinloc >= firstchange)
7140 ) {
7141 *pp = cmdp->next;
7142 free(cmdp);
7143 } else {
7144 pp = &cmdp->next;
7145 }
7146 }
7147 }
7148 INT_ON;
7149}
7150
7151/*
7152 * Locate a command in the command hash table. If "add" is nonzero,
7153 * add the command to the table if it is not already present. The
7154 * variable "lastcmdentry" is set to point to the address of the link
7155 * pointing to the entry, so that delete_cmd_entry can delete the
7156 * entry.
7157 *
7158 * Interrupts must be off if called with add != 0.
7159 */
7160static struct tblentry **lastcmdentry;
7161
7162static struct tblentry *
7163cmdlookup(const char *name, int add)
7164{
7165 unsigned int hashval;
7166 const char *p;
7167 struct tblentry *cmdp;
7168 struct tblentry **pp;
7169
7170 p = name;
7171 hashval = (unsigned char)*p << 4;
7172 while (*p)
7173 hashval += (unsigned char)*p++;
7174 hashval &= 0x7FFF;
7175 pp = &cmdtable[hashval % CMDTABLESIZE];
7176 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7177 if (strcmp(cmdp->cmdname, name) == 0)
7178 break;
7179 pp = &cmdp->next;
7180 }
7181 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007182 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7183 + strlen(name)
7184 /* + 1 - already done because
7185 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007186 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007187 cmdp->cmdtype = CMDUNKNOWN;
7188 strcpy(cmdp->cmdname, name);
7189 }
7190 lastcmdentry = pp;
7191 return cmdp;
7192}
7193
7194/*
7195 * Delete the command entry returned on the last lookup.
7196 */
7197static void
7198delete_cmd_entry(void)
7199{
7200 struct tblentry *cmdp;
7201
7202 INT_OFF;
7203 cmdp = *lastcmdentry;
7204 *lastcmdentry = cmdp->next;
7205 if (cmdp->cmdtype == CMDFUNCTION)
7206 freefunc(cmdp->param.func);
7207 free(cmdp);
7208 INT_ON;
7209}
7210
7211/*
7212 * Add a new command entry, replacing any existing command entry for
7213 * the same name - except special builtins.
7214 */
7215static void
7216addcmdentry(char *name, struct cmdentry *entry)
7217{
7218 struct tblentry *cmdp;
7219
7220 cmdp = cmdlookup(name, 1);
7221 if (cmdp->cmdtype == CMDFUNCTION) {
7222 freefunc(cmdp->param.func);
7223 }
7224 cmdp->cmdtype = entry->cmdtype;
7225 cmdp->param = entry->u;
7226 cmdp->rehash = 0;
7227}
7228
7229static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007230hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007231{
7232 struct tblentry **pp;
7233 struct tblentry *cmdp;
7234 int c;
7235 struct cmdentry entry;
7236 char *name;
7237
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007238 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007239 clearcmdentry(0);
7240 return 0;
7241 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007242
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007243 if (*argptr == NULL) {
7244 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7245 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7246 if (cmdp->cmdtype == CMDNORMAL)
7247 printentry(cmdp);
7248 }
7249 }
7250 return 0;
7251 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007252
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007253 c = 0;
7254 while ((name = *argptr) != NULL) {
7255 cmdp = cmdlookup(name, 0);
7256 if (cmdp != NULL
7257 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007258 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7259 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007260 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007261 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007262 find_command(name, &entry, DO_ERR, pathval());
7263 if (entry.cmdtype == CMDUNKNOWN)
7264 c = 1;
7265 argptr++;
7266 }
7267 return c;
7268}
7269
7270/*
7271 * Called when a cd is done. Marks all commands so the next time they
7272 * are executed they will be rehashed.
7273 */
7274static void
7275hashcd(void)
7276{
7277 struct tblentry **pp;
7278 struct tblentry *cmdp;
7279
7280 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7281 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007282 if (cmdp->cmdtype == CMDNORMAL
7283 || (cmdp->cmdtype == CMDBUILTIN
7284 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7285 && builtinloc > 0)
7286 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007288 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007289 }
7290 }
7291}
7292
7293/*
7294 * Fix command hash table when PATH changed.
7295 * Called before PATH is changed. The argument is the new value of PATH;
7296 * pathval() still returns the old value at this point.
7297 * Called with interrupts off.
7298 */
7299static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007300changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007301{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007302 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007303 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007304 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007305 int idx_bltin;
7306
7307 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007308 firstchange = 9999; /* assume no change */
7309 idx = 0;
7310 idx_bltin = -1;
7311 for (;;) {
7312 if (*old != *new) {
7313 firstchange = idx;
7314 if ((*old == '\0' && *new == ':')
7315 || (*old == ':' && *new == '\0'))
7316 firstchange++;
7317 old = new; /* ignore subsequent differences */
7318 }
7319 if (*new == '\0')
7320 break;
7321 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7322 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007323 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007324 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007325 new++, old++;
7326 }
7327 if (builtinloc < 0 && idx_bltin >= 0)
7328 builtinloc = idx_bltin; /* zap builtins */
7329 if (builtinloc >= 0 && idx_bltin < 0)
7330 firstchange = 0;
7331 clearcmdentry(firstchange);
7332 builtinloc = idx_bltin;
7333}
7334
7335#define TEOF 0
7336#define TNL 1
7337#define TREDIR 2
7338#define TWORD 3
7339#define TSEMI 4
7340#define TBACKGND 5
7341#define TAND 6
7342#define TOR 7
7343#define TPIPE 8
7344#define TLP 9
7345#define TRP 10
7346#define TENDCASE 11
7347#define TENDBQUOTE 12
7348#define TNOT 13
7349#define TCASE 14
7350#define TDO 15
7351#define TDONE 16
7352#define TELIF 17
7353#define TELSE 18
7354#define TESAC 19
7355#define TFI 20
7356#define TFOR 21
7357#define TIF 22
7358#define TIN 23
7359#define TTHEN 24
7360#define TUNTIL 25
7361#define TWHILE 26
7362#define TBEGIN 27
7363#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007364typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007365
7366/* first char is indicating which tokens mark the end of a list */
7367static const char *const tokname_array[] = {
7368 "\1end of file",
7369 "\0newline",
7370 "\0redirection",
7371 "\0word",
7372 "\0;",
7373 "\0&",
7374 "\0&&",
7375 "\0||",
7376 "\0|",
7377 "\0(",
7378 "\1)",
7379 "\1;;",
7380 "\1`",
7381#define KWDOFFSET 13
7382 /* the following are keywords */
7383 "\0!",
7384 "\0case",
7385 "\1do",
7386 "\1done",
7387 "\1elif",
7388 "\1else",
7389 "\1esac",
7390 "\1fi",
7391 "\0for",
7392 "\0if",
7393 "\0in",
7394 "\1then",
7395 "\0until",
7396 "\0while",
7397 "\0{",
7398 "\1}",
7399};
7400
7401static const char *
7402tokname(int tok)
7403{
7404 static char buf[16];
7405
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007406//try this:
7407//if (tok < TSEMI) return tokname_array[tok] + 1;
7408//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7409//return buf;
7410
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007411 if (tok >= TSEMI)
7412 buf[0] = '"';
7413 sprintf(buf + (tok >= TSEMI), "%s%c",
7414 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7415 return buf;
7416}
7417
7418/* Wrapper around strcmp for qsort/bsearch/... */
7419static int
7420pstrcmp(const void *a, const void *b)
7421{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007422 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007423}
7424
7425static const char *const *
7426findkwd(const char *s)
7427{
7428 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007429 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7430 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007431}
7432
7433/*
7434 * Locate and print what a word is...
7435 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007436static int
7437describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007438{
7439 struct cmdentry entry;
7440 struct tblentry *cmdp;
7441#if ENABLE_ASH_ALIAS
7442 const struct alias *ap;
7443#endif
7444 const char *path = pathval();
7445
7446 if (describe_command_verbose) {
7447 out1str(command);
7448 }
7449
7450 /* First look at the keywords */
7451 if (findkwd(command)) {
7452 out1str(describe_command_verbose ? " is a shell keyword" : command);
7453 goto out;
7454 }
7455
7456#if ENABLE_ASH_ALIAS
7457 /* Then look at the aliases */
7458 ap = lookupalias(command, 0);
7459 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007460 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007461 out1str("alias ");
7462 printalias(ap);
7463 return 0;
7464 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007465 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007466 goto out;
7467 }
7468#endif
7469 /* Then check if it is a tracked alias */
7470 cmdp = cmdlookup(command, 0);
7471 if (cmdp != NULL) {
7472 entry.cmdtype = cmdp->cmdtype;
7473 entry.u = cmdp->param;
7474 } else {
7475 /* Finally use brute force */
7476 find_command(command, &entry, DO_ABS, path);
7477 }
7478
7479 switch (entry.cmdtype) {
7480 case CMDNORMAL: {
7481 int j = entry.u.index;
7482 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007483 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007484 p = command;
7485 } else {
7486 do {
7487 p = padvance(&path, command);
7488 stunalloc(p);
7489 } while (--j >= 0);
7490 }
7491 if (describe_command_verbose) {
7492 out1fmt(" is%s %s",
7493 (cmdp ? " a tracked alias for" : nullstr), p
7494 );
7495 } else {
7496 out1str(p);
7497 }
7498 break;
7499 }
7500
7501 case CMDFUNCTION:
7502 if (describe_command_verbose) {
7503 out1str(" is a shell function");
7504 } else {
7505 out1str(command);
7506 }
7507 break;
7508
7509 case CMDBUILTIN:
7510 if (describe_command_verbose) {
7511 out1fmt(" is a %sshell builtin",
7512 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7513 "special " : nullstr
7514 );
7515 } else {
7516 out1str(command);
7517 }
7518 break;
7519
7520 default:
7521 if (describe_command_verbose) {
7522 out1str(": not found\n");
7523 }
7524 return 127;
7525 }
7526 out:
7527 outstr("\n", stdout);
7528 return 0;
7529}
7530
7531static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007532typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007533{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007534 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007535 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007536 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007537
Denis Vlasenko46846e22007-05-20 13:08:31 +00007538 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007539 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007540 i++;
7541 verbose = 0;
7542 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007543 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007544 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007545 }
7546 return err;
7547}
7548
7549#if ENABLE_ASH_CMDCMD
7550static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007551commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007552{
7553 int c;
7554 enum {
7555 VERIFY_BRIEF = 1,
7556 VERIFY_VERBOSE = 2,
7557 } verify = 0;
7558
7559 while ((c = nextopt("pvV")) != '\0')
7560 if (c == 'V')
7561 verify |= VERIFY_VERBOSE;
7562 else if (c == 'v')
7563 verify |= VERIFY_BRIEF;
7564#if DEBUG
7565 else if (c != 'p')
7566 abort();
7567#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007568 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7569 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007570 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007571 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007572
7573 return 0;
7574}
7575#endif
7576
7577
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007578/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007579
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007580static int funcblocksize; /* size of structures in function */
7581static int funcstringsize; /* size of strings in node */
7582static void *funcblock; /* block to allocate function from */
7583static char *funcstring; /* block to allocate strings from */
7584
Eric Andersencb57d552001-06-28 07:25:16 +00007585/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007586#define EV_EXIT 01 /* exit after evaluating tree */
7587#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7588#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007589
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007590static const short nodesize[26] = {
7591 SHELL_ALIGN(sizeof(struct ncmd)),
7592 SHELL_ALIGN(sizeof(struct npipe)),
7593 SHELL_ALIGN(sizeof(struct nredir)),
7594 SHELL_ALIGN(sizeof(struct nredir)),
7595 SHELL_ALIGN(sizeof(struct nredir)),
7596 SHELL_ALIGN(sizeof(struct nbinary)),
7597 SHELL_ALIGN(sizeof(struct nbinary)),
7598 SHELL_ALIGN(sizeof(struct nbinary)),
7599 SHELL_ALIGN(sizeof(struct nif)),
7600 SHELL_ALIGN(sizeof(struct nbinary)),
7601 SHELL_ALIGN(sizeof(struct nbinary)),
7602 SHELL_ALIGN(sizeof(struct nfor)),
7603 SHELL_ALIGN(sizeof(struct ncase)),
7604 SHELL_ALIGN(sizeof(struct nclist)),
7605 SHELL_ALIGN(sizeof(struct narg)),
7606 SHELL_ALIGN(sizeof(struct narg)),
7607 SHELL_ALIGN(sizeof(struct nfile)),
7608 SHELL_ALIGN(sizeof(struct nfile)),
7609 SHELL_ALIGN(sizeof(struct nfile)),
7610 SHELL_ALIGN(sizeof(struct nfile)),
7611 SHELL_ALIGN(sizeof(struct nfile)),
7612 SHELL_ALIGN(sizeof(struct ndup)),
7613 SHELL_ALIGN(sizeof(struct ndup)),
7614 SHELL_ALIGN(sizeof(struct nhere)),
7615 SHELL_ALIGN(sizeof(struct nhere)),
7616 SHELL_ALIGN(sizeof(struct nnot)),
7617};
7618
7619static void calcsize(union node *n);
7620
7621static void
7622sizenodelist(struct nodelist *lp)
7623{
7624 while (lp) {
7625 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7626 calcsize(lp->n);
7627 lp = lp->next;
7628 }
7629}
7630
7631static void
7632calcsize(union node *n)
7633{
7634 if (n == NULL)
7635 return;
7636 funcblocksize += nodesize[n->type];
7637 switch (n->type) {
7638 case NCMD:
7639 calcsize(n->ncmd.redirect);
7640 calcsize(n->ncmd.args);
7641 calcsize(n->ncmd.assign);
7642 break;
7643 case NPIPE:
7644 sizenodelist(n->npipe.cmdlist);
7645 break;
7646 case NREDIR:
7647 case NBACKGND:
7648 case NSUBSHELL:
7649 calcsize(n->nredir.redirect);
7650 calcsize(n->nredir.n);
7651 break;
7652 case NAND:
7653 case NOR:
7654 case NSEMI:
7655 case NWHILE:
7656 case NUNTIL:
7657 calcsize(n->nbinary.ch2);
7658 calcsize(n->nbinary.ch1);
7659 break;
7660 case NIF:
7661 calcsize(n->nif.elsepart);
7662 calcsize(n->nif.ifpart);
7663 calcsize(n->nif.test);
7664 break;
7665 case NFOR:
7666 funcstringsize += strlen(n->nfor.var) + 1;
7667 calcsize(n->nfor.body);
7668 calcsize(n->nfor.args);
7669 break;
7670 case NCASE:
7671 calcsize(n->ncase.cases);
7672 calcsize(n->ncase.expr);
7673 break;
7674 case NCLIST:
7675 calcsize(n->nclist.body);
7676 calcsize(n->nclist.pattern);
7677 calcsize(n->nclist.next);
7678 break;
7679 case NDEFUN:
7680 case NARG:
7681 sizenodelist(n->narg.backquote);
7682 funcstringsize += strlen(n->narg.text) + 1;
7683 calcsize(n->narg.next);
7684 break;
7685 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007686#if ENABLE_ASH_BASH_COMPAT
7687 case NTO2:
7688#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007689 case NCLOBBER:
7690 case NFROM:
7691 case NFROMTO:
7692 case NAPPEND:
7693 calcsize(n->nfile.fname);
7694 calcsize(n->nfile.next);
7695 break;
7696 case NTOFD:
7697 case NFROMFD:
7698 calcsize(n->ndup.vname);
7699 calcsize(n->ndup.next);
7700 break;
7701 case NHERE:
7702 case NXHERE:
7703 calcsize(n->nhere.doc);
7704 calcsize(n->nhere.next);
7705 break;
7706 case NNOT:
7707 calcsize(n->nnot.com);
7708 break;
7709 };
7710}
7711
7712static char *
7713nodeckstrdup(char *s)
7714{
7715 char *rtn = funcstring;
7716
7717 strcpy(funcstring, s);
7718 funcstring += strlen(s) + 1;
7719 return rtn;
7720}
7721
7722static union node *copynode(union node *);
7723
7724static struct nodelist *
7725copynodelist(struct nodelist *lp)
7726{
7727 struct nodelist *start;
7728 struct nodelist **lpp;
7729
7730 lpp = &start;
7731 while (lp) {
7732 *lpp = funcblock;
7733 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7734 (*lpp)->n = copynode(lp->n);
7735 lp = lp->next;
7736 lpp = &(*lpp)->next;
7737 }
7738 *lpp = NULL;
7739 return start;
7740}
7741
7742static union node *
7743copynode(union node *n)
7744{
7745 union node *new;
7746
7747 if (n == NULL)
7748 return NULL;
7749 new = funcblock;
7750 funcblock = (char *) funcblock + nodesize[n->type];
7751
7752 switch (n->type) {
7753 case NCMD:
7754 new->ncmd.redirect = copynode(n->ncmd.redirect);
7755 new->ncmd.args = copynode(n->ncmd.args);
7756 new->ncmd.assign = copynode(n->ncmd.assign);
7757 break;
7758 case NPIPE:
7759 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007760 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007761 break;
7762 case NREDIR:
7763 case NBACKGND:
7764 case NSUBSHELL:
7765 new->nredir.redirect = copynode(n->nredir.redirect);
7766 new->nredir.n = copynode(n->nredir.n);
7767 break;
7768 case NAND:
7769 case NOR:
7770 case NSEMI:
7771 case NWHILE:
7772 case NUNTIL:
7773 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7774 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7775 break;
7776 case NIF:
7777 new->nif.elsepart = copynode(n->nif.elsepart);
7778 new->nif.ifpart = copynode(n->nif.ifpart);
7779 new->nif.test = copynode(n->nif.test);
7780 break;
7781 case NFOR:
7782 new->nfor.var = nodeckstrdup(n->nfor.var);
7783 new->nfor.body = copynode(n->nfor.body);
7784 new->nfor.args = copynode(n->nfor.args);
7785 break;
7786 case NCASE:
7787 new->ncase.cases = copynode(n->ncase.cases);
7788 new->ncase.expr = copynode(n->ncase.expr);
7789 break;
7790 case NCLIST:
7791 new->nclist.body = copynode(n->nclist.body);
7792 new->nclist.pattern = copynode(n->nclist.pattern);
7793 new->nclist.next = copynode(n->nclist.next);
7794 break;
7795 case NDEFUN:
7796 case NARG:
7797 new->narg.backquote = copynodelist(n->narg.backquote);
7798 new->narg.text = nodeckstrdup(n->narg.text);
7799 new->narg.next = copynode(n->narg.next);
7800 break;
7801 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007802#if ENABLE_ASH_BASH_COMPAT
7803 case NTO2:
7804#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007805 case NCLOBBER:
7806 case NFROM:
7807 case NFROMTO:
7808 case NAPPEND:
7809 new->nfile.fname = copynode(n->nfile.fname);
7810 new->nfile.fd = n->nfile.fd;
7811 new->nfile.next = copynode(n->nfile.next);
7812 break;
7813 case NTOFD:
7814 case NFROMFD:
7815 new->ndup.vname = copynode(n->ndup.vname);
7816 new->ndup.dupfd = n->ndup.dupfd;
7817 new->ndup.fd = n->ndup.fd;
7818 new->ndup.next = copynode(n->ndup.next);
7819 break;
7820 case NHERE:
7821 case NXHERE:
7822 new->nhere.doc = copynode(n->nhere.doc);
7823 new->nhere.fd = n->nhere.fd;
7824 new->nhere.next = copynode(n->nhere.next);
7825 break;
7826 case NNOT:
7827 new->nnot.com = copynode(n->nnot.com);
7828 break;
7829 };
7830 new->type = n->type;
7831 return new;
7832}
7833
7834/*
7835 * Make a copy of a parse tree.
7836 */
7837static struct funcnode *
7838copyfunc(union node *n)
7839{
7840 struct funcnode *f;
7841 size_t blocksize;
7842
7843 funcblocksize = offsetof(struct funcnode, n);
7844 funcstringsize = 0;
7845 calcsize(n);
7846 blocksize = funcblocksize;
7847 f = ckmalloc(blocksize + funcstringsize);
7848 funcblock = (char *) f + offsetof(struct funcnode, n);
7849 funcstring = (char *) f + blocksize;
7850 copynode(n);
7851 f->count = 0;
7852 return f;
7853}
7854
7855/*
7856 * Define a shell function.
7857 */
7858static void
7859defun(char *name, union node *func)
7860{
7861 struct cmdentry entry;
7862
7863 INT_OFF;
7864 entry.cmdtype = CMDFUNCTION;
7865 entry.u.func = copyfunc(func);
7866 addcmdentry(name, &entry);
7867 INT_ON;
7868}
7869
7870static int evalskip; /* set if we are skipping commands */
7871/* reasons for skipping commands (see comment on breakcmd routine) */
7872#define SKIPBREAK (1 << 0)
7873#define SKIPCONT (1 << 1)
7874#define SKIPFUNC (1 << 2)
7875#define SKIPFILE (1 << 3)
7876#define SKIPEVAL (1 << 4)
7877static int skipcount; /* number of levels to skip */
7878static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007879static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007880
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007881/* forward decl way out to parsing code - dotrap needs it */
7882static int evalstring(char *s, int mask);
7883
7884/*
7885 * Called to execute a trap. Perhaps we should avoid entering new trap
7886 * handlers while we are executing a trap handler.
7887 */
7888static int
7889dotrap(void)
7890{
7891 char *p;
7892 char *q;
7893 int i;
7894 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007895 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007896
7897 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007898 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007899 xbarrier();
7900
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007901 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007902 if (!*q)
7903 continue;
7904 *q = '\0';
7905
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007906 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007907 if (!p)
7908 continue;
7909 skip = evalstring(p, SKIPEVAL);
7910 exitstatus = savestatus;
7911 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007912 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007913 }
7914
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007915 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007916}
7917
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007918/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007919static void evalloop(union node *, int);
7920static void evalfor(union node *, int);
7921static void evalcase(union node *, int);
7922static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007923static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007924static void evalpipe(union node *, int);
7925static void evalcommand(union node *, int);
7926static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007927static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007928
Eric Andersen62483552001-07-10 06:09:16 +00007929/*
Eric Andersenc470f442003-07-28 09:56:35 +00007930 * Evaluate a parse tree. The value is left in the global variable
7931 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007932 */
Eric Andersenc470f442003-07-28 09:56:35 +00007933static void
7934evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007935{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007936
7937 struct jmploc *volatile savehandler = exception_handler;
7938 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007939 int checkexit = 0;
7940 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007941 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007942
Eric Andersenc470f442003-07-28 09:56:35 +00007943 if (n == NULL) {
7944 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007945 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007946 }
Eric Andersenc470f442003-07-28 09:56:35 +00007947 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007948 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007949
7950 exception_handler = &jmploc;
7951 {
7952 int err = setjmp(jmploc.loc);
7953 if (err) {
7954 /* if it was a signal, check for trap handlers */
7955 if (exception == EXSIG)
7956 goto out;
7957 /* continue on the way out */
7958 exception_handler = savehandler;
7959 longjmp(exception_handler->loc, err);
7960 }
7961 }
7962
Eric Andersenc470f442003-07-28 09:56:35 +00007963 switch (n->type) {
7964 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007965#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007966 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007967 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007968 break;
7969#endif
7970 case NNOT:
7971 evaltree(n->nnot.com, EV_TESTED);
7972 status = !exitstatus;
7973 goto setstatus;
7974 case NREDIR:
7975 expredir(n->nredir.redirect);
7976 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7977 if (!status) {
7978 evaltree(n->nredir.n, flags & EV_TESTED);
7979 status = exitstatus;
7980 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007981 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00007982 goto setstatus;
7983 case NCMD:
7984 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007985 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007986 if (eflag && !(flags & EV_TESTED))
7987 checkexit = ~0;
7988 goto calleval;
7989 case NFOR:
7990 evalfn = evalfor;
7991 goto calleval;
7992 case NWHILE:
7993 case NUNTIL:
7994 evalfn = evalloop;
7995 goto calleval;
7996 case NSUBSHELL:
7997 case NBACKGND:
7998 evalfn = evalsubshell;
7999 goto calleval;
8000 case NPIPE:
8001 evalfn = evalpipe;
8002 goto checkexit;
8003 case NCASE:
8004 evalfn = evalcase;
8005 goto calleval;
8006 case NAND:
8007 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008008 case NSEMI: {
8009
Eric Andersenc470f442003-07-28 09:56:35 +00008010#if NAND + 1 != NOR
8011#error NAND + 1 != NOR
8012#endif
8013#if NOR + 1 != NSEMI
8014#error NOR + 1 != NSEMI
8015#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008016 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008017 evaltree(
8018 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008019 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008020 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008021 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008022 break;
8023 if (!evalskip) {
8024 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008025 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008026 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008027 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008028 evalfn(n, flags);
8029 break;
8030 }
8031 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008032 }
Eric Andersenc470f442003-07-28 09:56:35 +00008033 case NIF:
8034 evaltree(n->nif.test, EV_TESTED);
8035 if (evalskip)
8036 break;
8037 if (exitstatus == 0) {
8038 n = n->nif.ifpart;
8039 goto evaln;
8040 } else if (n->nif.elsepart) {
8041 n = n->nif.elsepart;
8042 goto evaln;
8043 }
8044 goto success;
8045 case NDEFUN:
8046 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008047 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008048 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008049 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008050 exitstatus = status;
8051 break;
8052 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008053
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008054 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008055 exception_handler = savehandler;
8056 out1:
8057 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008058 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008059 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008060 goto exexit;
8061
8062 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008063 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008064 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008065 }
Eric Andersen62483552001-07-10 06:09:16 +00008066}
8067
Eric Andersenc470f442003-07-28 09:56:35 +00008068#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8069static
8070#endif
8071void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8072
Eric Andersenc470f442003-07-28 09:56:35 +00008073static void
8074evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008075{
8076 int status;
8077
8078 loopnest++;
8079 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008080 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008081 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008082 int i;
8083
Eric Andersencb57d552001-06-28 07:25:16 +00008084 evaltree(n->nbinary.ch1, EV_TESTED);
8085 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008086 skipping:
8087 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008088 evalskip = 0;
8089 continue;
8090 }
8091 if (evalskip == SKIPBREAK && --skipcount <= 0)
8092 evalskip = 0;
8093 break;
8094 }
Eric Andersenc470f442003-07-28 09:56:35 +00008095 i = exitstatus;
8096 if (n->type != NWHILE)
8097 i = !i;
8098 if (i != 0)
8099 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008100 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008101 status = exitstatus;
8102 if (evalskip)
8103 goto skipping;
8104 }
8105 loopnest--;
8106 exitstatus = status;
8107}
8108
Eric Andersenc470f442003-07-28 09:56:35 +00008109static void
8110evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008111{
8112 struct arglist arglist;
8113 union node *argp;
8114 struct strlist *sp;
8115 struct stackmark smark;
8116
8117 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008118 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008119 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008120 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008121 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008122 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008123 if (evalskip)
8124 goto out;
8125 }
8126 *arglist.lastp = NULL;
8127
8128 exitstatus = 0;
8129 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008130 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008131 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008132 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008133 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008134 if (evalskip) {
8135 if (evalskip == SKIPCONT && --skipcount <= 0) {
8136 evalskip = 0;
8137 continue;
8138 }
8139 if (evalskip == SKIPBREAK && --skipcount <= 0)
8140 evalskip = 0;
8141 break;
8142 }
8143 }
8144 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008145 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008146 popstackmark(&smark);
8147}
8148
Eric Andersenc470f442003-07-28 09:56:35 +00008149static void
8150evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008151{
8152 union node *cp;
8153 union node *patp;
8154 struct arglist arglist;
8155 struct stackmark smark;
8156
8157 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008158 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008159 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008160 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008161 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008162 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8163 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008164 if (casematch(patp, arglist.list->text)) {
8165 if (evalskip == 0) {
8166 evaltree(cp->nclist.body, flags);
8167 }
8168 goto out;
8169 }
8170 }
8171 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008172 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008173 popstackmark(&smark);
8174}
8175
Eric Andersenc470f442003-07-28 09:56:35 +00008176/*
8177 * Kick off a subshell to evaluate a tree.
8178 */
Eric Andersenc470f442003-07-28 09:56:35 +00008179static void
8180evalsubshell(union node *n, int flags)
8181{
8182 struct job *jp;
8183 int backgnd = (n->type == NBACKGND);
8184 int status;
8185
8186 expredir(n->nredir.redirect);
8187 if (!backgnd && flags & EV_EXIT && !trap[0])
8188 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008189 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008190 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008191 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008192 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008193 flags |= EV_EXIT;
8194 if (backgnd)
8195 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008196 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008197 redirect(n->nredir.redirect, 0);
8198 evaltreenr(n->nredir.n, flags);
8199 /* never returns */
8200 }
8201 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008202 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008203 status = waitforjob(jp);
8204 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008205 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008206}
8207
Eric Andersenc470f442003-07-28 09:56:35 +00008208/*
8209 * Compute the names of the files in a redirection list.
8210 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008211static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008212static void
8213expredir(union node *n)
8214{
8215 union node *redir;
8216
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008217 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008218 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008219
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008220 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008221 fn.lastp = &fn.list;
8222 switch (redir->type) {
8223 case NFROMTO:
8224 case NFROM:
8225 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008226#if ENABLE_ASH_BASH_COMPAT
8227 case NTO2:
8228#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008229 case NCLOBBER:
8230 case NAPPEND:
8231 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008232#if ENABLE_ASH_BASH_COMPAT
8233 store_expfname:
8234#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008235 redir->nfile.expfname = fn.list->text;
8236 break;
8237 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008238 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008239 if (redir->ndup.vname) {
8240 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008241 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008242 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008243#if ENABLE_ASH_BASH_COMPAT
8244//FIXME: we used expandarg with different args!
8245 if (!isdigit_str9(fn.list->text)) {
8246 /* >&file, not >&fd */
8247 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8248 ash_msg_and_raise_error("redir error");
8249 redir->type = NTO2;
8250 goto store_expfname;
8251 }
8252#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008253 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008254 }
8255 break;
8256 }
8257 }
8258}
8259
Eric Andersencb57d552001-06-28 07:25:16 +00008260/*
Eric Andersencb57d552001-06-28 07:25:16 +00008261 * Evaluate a pipeline. All the processes in the pipeline are children
8262 * of the process creating the pipeline. (This differs from some versions
8263 * of the shell, which make the last process in a pipeline the parent
8264 * of all the rest.)
8265 */
Eric Andersenc470f442003-07-28 09:56:35 +00008266static void
8267evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008268{
8269 struct job *jp;
8270 struct nodelist *lp;
8271 int pipelen;
8272 int prevfd;
8273 int pip[2];
8274
Eric Andersenc470f442003-07-28 09:56:35 +00008275 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008276 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008277 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008278 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008279 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008280 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008281 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008282 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008283 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008284 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008285 pip[1] = -1;
8286 if (lp->next) {
8287 if (pipe(pip) < 0) {
8288 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008289 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008290 }
8291 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008292 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008293 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008294 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008295 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008296 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008297 if (prevfd > 0) {
8298 dup2(prevfd, 0);
8299 close(prevfd);
8300 }
8301 if (pip[1] > 1) {
8302 dup2(pip[1], 1);
8303 close(pip[1]);
8304 }
Eric Andersenc470f442003-07-28 09:56:35 +00008305 evaltreenr(lp->n, flags);
8306 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008307 }
8308 if (prevfd >= 0)
8309 close(prevfd);
8310 prevfd = pip[0];
8311 close(pip[1]);
8312 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008313 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008314 exitstatus = waitforjob(jp);
8315 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008316 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008317 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008318}
8319
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008320/*
8321 * Controls whether the shell is interactive or not.
8322 */
8323static void
8324setinteractive(int on)
8325{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008326 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008327
8328 if (++on == is_interactive)
8329 return;
8330 is_interactive = on;
8331 setsignal(SIGINT);
8332 setsignal(SIGQUIT);
8333 setsignal(SIGTERM);
8334#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8335 if (is_interactive > 1) {
8336 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008337 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008338
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008339 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008340 out1fmt(
8341 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008342 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008343 "Enter 'help' for a list of built-in commands."
8344 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008345 bb_banner);
8346 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008347 }
8348 }
8349#endif
8350}
8351
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008352static void
8353optschanged(void)
8354{
8355#if DEBUG
8356 opentrace();
8357#endif
8358 setinteractive(iflag);
8359 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008360#if ENABLE_FEATURE_EDITING_VI
8361 if (viflag)
8362 line_input_state->flags |= VI_MODE;
8363 else
8364 line_input_state->flags &= ~VI_MODE;
8365#else
8366 viflag = 0; /* forcibly keep the option off */
8367#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008368}
8369
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008370static struct localvar *localvars;
8371
8372/*
8373 * Called after a function returns.
8374 * Interrupts must be off.
8375 */
8376static void
8377poplocalvars(void)
8378{
8379 struct localvar *lvp;
8380 struct var *vp;
8381
8382 while ((lvp = localvars) != NULL) {
8383 localvars = lvp->next;
8384 vp = lvp->vp;
8385 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8386 if (vp == NULL) { /* $- saved */
8387 memcpy(optlist, lvp->text, sizeof(optlist));
8388 free((char*)lvp->text);
8389 optschanged();
8390 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8391 unsetvar(vp->text);
8392 } else {
8393 if (vp->func)
8394 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8395 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8396 free((char*)vp->text);
8397 vp->flags = lvp->flags;
8398 vp->text = lvp->text;
8399 }
8400 free(lvp);
8401 }
8402}
8403
8404static int
8405evalfun(struct funcnode *func, int argc, char **argv, int flags)
8406{
8407 volatile struct shparam saveparam;
8408 struct localvar *volatile savelocalvars;
8409 struct jmploc *volatile savehandler;
8410 struct jmploc jmploc;
8411 int e;
8412
8413 saveparam = shellparam;
8414 savelocalvars = localvars;
8415 e = setjmp(jmploc.loc);
8416 if (e) {
8417 goto funcdone;
8418 }
8419 INT_OFF;
8420 savehandler = exception_handler;
8421 exception_handler = &jmploc;
8422 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008423 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008424 func->count++;
8425 funcnest++;
8426 INT_ON;
8427 shellparam.nparam = argc - 1;
8428 shellparam.p = argv + 1;
8429#if ENABLE_ASH_GETOPTS
8430 shellparam.optind = 1;
8431 shellparam.optoff = -1;
8432#endif
8433 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008434 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008435 INT_OFF;
8436 funcnest--;
8437 freefunc(func);
8438 poplocalvars();
8439 localvars = savelocalvars;
8440 freeparam(&shellparam);
8441 shellparam = saveparam;
8442 exception_handler = savehandler;
8443 INT_ON;
8444 evalskip &= ~SKIPFUNC;
8445 return e;
8446}
8447
Denis Vlasenko131ae172007-02-18 13:00:19 +00008448#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008449static char **
8450parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008451{
8452 char *cp, c;
8453
8454 for (;;) {
8455 cp = *++argv;
8456 if (!cp)
8457 return 0;
8458 if (*cp++ != '-')
8459 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008460 c = *cp++;
8461 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008462 break;
8463 if (c == '-' && !*cp) {
8464 argv++;
8465 break;
8466 }
8467 do {
8468 switch (c) {
8469 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008470 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008471 break;
8472 default:
8473 /* run 'typecmd' for other options */
8474 return 0;
8475 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008476 c = *cp++;
8477 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008478 }
8479 return argv;
8480}
8481#endif
8482
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008483/*
8484 * Make a variable a local variable. When a variable is made local, it's
8485 * value and flags are saved in a localvar structure. The saved values
8486 * will be restored when the shell function returns. We handle the name
8487 * "-" as a special case.
8488 */
8489static void
8490mklocal(char *name)
8491{
8492 struct localvar *lvp;
8493 struct var **vpp;
8494 struct var *vp;
8495
8496 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008497 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008498 if (LONE_DASH(name)) {
8499 char *p;
8500 p = ckmalloc(sizeof(optlist));
8501 lvp->text = memcpy(p, optlist, sizeof(optlist));
8502 vp = NULL;
8503 } else {
8504 char *eq;
8505
8506 vpp = hashvar(name);
8507 vp = *findvar(vpp, name);
8508 eq = strchr(name, '=');
8509 if (vp == NULL) {
8510 if (eq)
8511 setvareq(name, VSTRFIXED);
8512 else
8513 setvar(name, NULL, VSTRFIXED);
8514 vp = *vpp; /* the new variable */
8515 lvp->flags = VUNSET;
8516 } else {
8517 lvp->text = vp->text;
8518 lvp->flags = vp->flags;
8519 vp->flags |= VSTRFIXED|VTEXTFIXED;
8520 if (eq)
8521 setvareq(name, 0);
8522 }
8523 }
8524 lvp->vp = vp;
8525 lvp->next = localvars;
8526 localvars = lvp;
8527 INT_ON;
8528}
8529
8530/*
8531 * The "local" command.
8532 */
8533static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008534localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008535{
8536 char *name;
8537
8538 argv = argptr;
8539 while ((name = *argv++) != NULL) {
8540 mklocal(name);
8541 }
8542 return 0;
8543}
8544
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008545static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008546falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008547{
8548 return 1;
8549}
8550
8551static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008552truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008553{
8554 return 0;
8555}
8556
8557static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008558execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008559{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008560 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008561 iflag = 0; /* exit on error */
8562 mflag = 0;
8563 optschanged();
8564 shellexec(argv + 1, pathval(), 0);
8565 }
8566 return 0;
8567}
8568
8569/*
8570 * The return command.
8571 */
8572static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008573returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008574{
8575 /*
8576 * If called outside a function, do what ksh does;
8577 * skip the rest of the file.
8578 */
8579 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8580 return argv[1] ? number(argv[1]) : exitstatus;
8581}
8582
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008583/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008584static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008585static int dotcmd(int, char **);
8586static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008587static int exitcmd(int, char **);
8588static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008589#if ENABLE_ASH_GETOPTS
8590static int getoptscmd(int, char **);
8591#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008592#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008593static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008594#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008595#if ENABLE_ASH_MATH_SUPPORT
8596static int letcmd(int, char **);
8597#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008598static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008599static int setcmd(int, char **);
8600static int shiftcmd(int, char **);
8601static int timescmd(int, char **);
8602static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008603static int umaskcmd(int, char **);
8604static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008605static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008606
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008607#define BUILTIN_NOSPEC "0"
8608#define BUILTIN_SPECIAL "1"
8609#define BUILTIN_REGULAR "2"
8610#define BUILTIN_SPEC_REG "3"
8611#define BUILTIN_ASSIGN "4"
8612#define BUILTIN_SPEC_ASSG "5"
8613#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008614#define BUILTIN_SPEC_REG_ASSG "7"
8615
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008616/* We do not handle [[ expr ]] bashism bash-compatibly,
8617 * we make it a synonym of [ expr ].
8618 * Basically, word splitting and pathname expansion should NOT be performed
8619 * Examples:
8620 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8621 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8622 * Additional operators:
8623 * || and && should work as -o and -a
8624 * =~ regexp match
8625 * Apart from the above, [[ expr ]] should work as [ expr ]
8626 */
8627
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008628#define echocmd echo_main
8629#define printfcmd printf_main
8630#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008631
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008632/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008633static const struct builtincmd builtintab[] = {
8634 { BUILTIN_SPEC_REG ".", dotcmd },
8635 { BUILTIN_SPEC_REG ":", truecmd },
8636#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008637 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008638#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008639 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008640#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008641#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008642#if ENABLE_ASH_ALIAS
8643 { BUILTIN_REG_ASSG "alias", aliascmd },
8644#endif
8645#if JOBS
8646 { BUILTIN_REGULAR "bg", fg_bgcmd },
8647#endif
8648 { BUILTIN_SPEC_REG "break", breakcmd },
8649 { BUILTIN_REGULAR "cd", cdcmd },
8650 { BUILTIN_NOSPEC "chdir", cdcmd },
8651#if ENABLE_ASH_CMDCMD
8652 { BUILTIN_REGULAR "command", commandcmd },
8653#endif
8654 { BUILTIN_SPEC_REG "continue", breakcmd },
8655#if ENABLE_ASH_BUILTIN_ECHO
8656 { BUILTIN_REGULAR "echo", echocmd },
8657#endif
8658 { BUILTIN_SPEC_REG "eval", evalcmd },
8659 { BUILTIN_SPEC_REG "exec", execcmd },
8660 { BUILTIN_SPEC_REG "exit", exitcmd },
8661 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8662 { BUILTIN_REGULAR "false", falsecmd },
8663#if JOBS
8664 { BUILTIN_REGULAR "fg", fg_bgcmd },
8665#endif
8666#if ENABLE_ASH_GETOPTS
8667 { BUILTIN_REGULAR "getopts", getoptscmd },
8668#endif
8669 { BUILTIN_NOSPEC "hash", hashcmd },
8670#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8671 { BUILTIN_NOSPEC "help", helpcmd },
8672#endif
8673#if JOBS
8674 { BUILTIN_REGULAR "jobs", jobscmd },
8675 { BUILTIN_REGULAR "kill", killcmd },
8676#endif
8677#if ENABLE_ASH_MATH_SUPPORT
8678 { BUILTIN_NOSPEC "let", letcmd },
8679#endif
8680 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008681#if ENABLE_ASH_BUILTIN_PRINTF
8682 { BUILTIN_REGULAR "printf", printfcmd },
8683#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008684 { BUILTIN_NOSPEC "pwd", pwdcmd },
8685 { BUILTIN_REGULAR "read", readcmd },
8686 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8687 { BUILTIN_SPEC_REG "return", returncmd },
8688 { BUILTIN_SPEC_REG "set", setcmd },
8689 { BUILTIN_SPEC_REG "shift", shiftcmd },
8690 { BUILTIN_SPEC_REG "source", dotcmd },
8691#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008692 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008693#endif
8694 { BUILTIN_SPEC_REG "times", timescmd },
8695 { BUILTIN_SPEC_REG "trap", trapcmd },
8696 { BUILTIN_REGULAR "true", truecmd },
8697 { BUILTIN_NOSPEC "type", typecmd },
8698 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8699 { BUILTIN_REGULAR "umask", umaskcmd },
8700#if ENABLE_ASH_ALIAS
8701 { BUILTIN_REGULAR "unalias", unaliascmd },
8702#endif
8703 { BUILTIN_SPEC_REG "unset", unsetcmd },
8704 { BUILTIN_REGULAR "wait", waitcmd },
8705};
8706
Denis Vlasenko80591b02008-03-25 07:49:43 +00008707/* Should match the above table! */
8708#define COMMANDCMD (builtintab + \
8709 2 + \
8710 1 * ENABLE_ASH_BUILTIN_TEST + \
8711 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8712 1 * ENABLE_ASH_ALIAS + \
8713 1 * ENABLE_ASH_JOB_CONTROL + \
8714 3)
8715#define EXECCMD (builtintab + \
8716 2 + \
8717 1 * ENABLE_ASH_BUILTIN_TEST + \
8718 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8719 1 * ENABLE_ASH_ALIAS + \
8720 1 * ENABLE_ASH_JOB_CONTROL + \
8721 3 + \
8722 1 * ENABLE_ASH_CMDCMD + \
8723 1 + \
8724 ENABLE_ASH_BUILTIN_ECHO + \
8725 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008726
8727/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008728 * Search the table of builtin commands.
8729 */
8730static struct builtincmd *
8731find_builtin(const char *name)
8732{
8733 struct builtincmd *bp;
8734
8735 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008736 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008737 pstrcmp
8738 );
8739 return bp;
8740}
8741
8742/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008743 * Execute a simple command.
8744 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008745static int
8746isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008747{
8748 const char *q = endofname(p);
8749 if (p == q)
8750 return 0;
8751 return *q == '=';
8752}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008753static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008754bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008755{
8756 /* Preserve exitstatus of a previous possible redirection
8757 * as POSIX mandates */
8758 return back_exitstatus;
8759}
Eric Andersenc470f442003-07-28 09:56:35 +00008760static void
8761evalcommand(union node *cmd, int flags)
8762{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008763 static const struct builtincmd null_bltin = {
8764 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008765 };
Eric Andersenc470f442003-07-28 09:56:35 +00008766 struct stackmark smark;
8767 union node *argp;
8768 struct arglist arglist;
8769 struct arglist varlist;
8770 char **argv;
8771 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008772 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008773 struct cmdentry cmdentry;
8774 struct job *jp;
8775 char *lastarg;
8776 const char *path;
8777 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008778 int status;
8779 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008780 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008781 smallint cmd_is_exec;
8782 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008783
8784 /* First expand the arguments. */
8785 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8786 setstackmark(&smark);
8787 back_exitstatus = 0;
8788
8789 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008790 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008791 varlist.lastp = &varlist.list;
8792 *varlist.lastp = NULL;
8793 arglist.lastp = &arglist.list;
8794 *arglist.lastp = NULL;
8795
8796 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008797 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008798 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8799 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8800 }
8801
Eric Andersenc470f442003-07-28 09:56:35 +00008802 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8803 struct strlist **spp;
8804
8805 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008806 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008807 expandarg(argp, &arglist, EXP_VARTILDE);
8808 else
8809 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8810
Eric Andersenc470f442003-07-28 09:56:35 +00008811 for (sp = *spp; sp; sp = sp->next)
8812 argc++;
8813 }
8814
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008815 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008816 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008817 TRACE(("evalcommand arg: %s\n", sp->text));
8818 *nargv++ = sp->text;
8819 }
8820 *nargv = NULL;
8821
8822 lastarg = NULL;
8823 if (iflag && funcnest == 0 && argc > 0)
8824 lastarg = nargv[-1];
8825
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008826 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008827 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008828 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008829
8830 path = vpath.text;
8831 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8832 struct strlist **spp;
8833 char *p;
8834
8835 spp = varlist.lastp;
8836 expandarg(argp, &varlist, EXP_VARTILDE);
8837
8838 /*
8839 * Modify the command lookup path, if a PATH= assignment
8840 * is present
8841 */
8842 p = (*spp)->text;
8843 if (varequal(p, path))
8844 path = p;
8845 }
8846
8847 /* Print the command if xflag is set. */
8848 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008849 int n;
8850 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008851
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008852 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008853 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008854
8855 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008856 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008857 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008858 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008859 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008860 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008861 p--;
8862 }
8863 }
8864 sp = arglist.list;
8865 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008866 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008867 }
8868
8869 cmd_is_exec = 0;
8870 spclbltin = -1;
8871
8872 /* Now locate the command. */
8873 if (argc) {
8874 const char *oldpath;
8875 int cmd_flag = DO_ERR;
8876
8877 path += 5;
8878 oldpath = path;
8879 for (;;) {
8880 find_command(argv[0], &cmdentry, cmd_flag, path);
8881 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008882 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008883 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008884 goto bail;
8885 }
8886
8887 /* implement bltin and command here */
8888 if (cmdentry.cmdtype != CMDBUILTIN)
8889 break;
8890 if (spclbltin < 0)
8891 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8892 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008893 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008894#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008895 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008896 path = oldpath;
8897 nargv = parse_command_args(argv, &path);
8898 if (!nargv)
8899 break;
8900 argc -= nargv - argv;
8901 argv = nargv;
8902 cmd_flag |= DO_NOFUNC;
8903 } else
8904#endif
8905 break;
8906 }
8907 }
8908
8909 if (status) {
8910 /* We have a redirection error. */
8911 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008912 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008913 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008914 exitstatus = status;
8915 goto out;
8916 }
8917
8918 /* Execute the command. */
8919 switch (cmdentry.cmdtype) {
8920 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008921#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008922 {
8923 /* find_command() encodes applet_no as (-2 - applet_no) */
8924 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008925 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008926 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008927 /* run <applet>_main() */
8928 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008929 break;
8930 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008931 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008932#endif
8933
Eric Andersenc470f442003-07-28 09:56:35 +00008934 /* Fork off a child process if necessary. */
8935 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008936 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008937 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008938 if (forkshell(jp, cmd, FORK_FG) != 0) {
8939 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008940 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008941 break;
8942 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008943 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008944 }
8945 listsetvar(varlist.list, VEXPORT|VSTACK);
8946 shellexec(argv, path, cmdentry.u.index);
8947 /* NOTREACHED */
8948
8949 case CMDBUILTIN:
8950 cmdenviron = varlist.list;
8951 if (cmdenviron) {
8952 struct strlist *list = cmdenviron;
8953 int i = VNOSET;
8954 if (spclbltin > 0 || argc == 0) {
8955 i = 0;
8956 if (cmd_is_exec && argc > 1)
8957 i = VEXPORT;
8958 }
8959 listsetvar(list, i);
8960 }
8961 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8962 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008963 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008964 if (i == EXEXIT)
8965 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008966 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008967 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008968 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008969 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008970 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008971 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008972 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008973 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008974 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008975 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008976 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008977 }
8978 break;
8979
8980 case CMDFUNCTION:
8981 listsetvar(varlist.list, 0);
8982 if (evalfun(cmdentry.u.func, argc, argv, flags))
8983 goto raise;
8984 break;
8985 }
8986
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008987 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008988 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008989 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008990 /* dsl: I think this is intended to be used to support
8991 * '_' in 'vi' command mode during line editing...
8992 * However I implemented that within libedit itself.
8993 */
8994 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008995 }
Eric Andersenc470f442003-07-28 09:56:35 +00008996 popstackmark(&smark);
8997}
8998
8999static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009000evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9001{
Eric Andersenc470f442003-07-28 09:56:35 +00009002 char *volatile savecmdname;
9003 struct jmploc *volatile savehandler;
9004 struct jmploc jmploc;
9005 int i;
9006
9007 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009008 i = setjmp(jmploc.loc);
9009 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009010 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009011 savehandler = exception_handler;
9012 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009013 commandname = argv[0];
9014 argptr = argv + 1;
9015 optptr = NULL; /* initialize nextopt */
9016 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009017 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009018 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009019 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009020 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009021 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009022// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009023 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009024
9025 return i;
9026}
9027
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009028static int
9029goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009030{
9031 return !*endofname(p);
9032}
9033
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009034
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009035/*
9036 * Search for a command. This is called before we fork so that the
9037 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009038 * the child. The check for "goodname" is an overly conservative
9039 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009040 */
Eric Andersenc470f442003-07-28 09:56:35 +00009041static void
9042prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009043{
9044 struct cmdentry entry;
9045
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009046 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9047 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009048}
9049
Eric Andersencb57d552001-06-28 07:25:16 +00009050
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009051/* ============ Builtin commands
9052 *
9053 * Builtin commands whose functions are closely tied to evaluation
9054 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009055 */
9056
9057/*
Eric Andersencb57d552001-06-28 07:25:16 +00009058 * Handle break and continue commands. Break, continue, and return are
9059 * all handled by setting the evalskip flag. The evaluation routines
9060 * above all check this flag, and if it is set they start skipping
9061 * commands rather than executing them. The variable skipcount is
9062 * the number of loops to break/continue, or the number of function
9063 * levels to return. (The latter is always 1.) It should probably
9064 * be an error to break out of more loops than exist, but it isn't
9065 * in the standard shell so we don't make it one here.
9066 */
Eric Andersenc470f442003-07-28 09:56:35 +00009067static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009068breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009069{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009070 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009071
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009072 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009073 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009074 if (n > loopnest)
9075 n = loopnest;
9076 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009077 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009078 skipcount = n;
9079 }
9080 return 0;
9081}
9082
Eric Andersenc470f442003-07-28 09:56:35 +00009083
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009084/* ============ input.c
9085 *
Eric Andersen90898442003-08-06 11:20:52 +00009086 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009087 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009088
Eric Andersenc470f442003-07-28 09:56:35 +00009089#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009090
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009091enum {
9092 INPUT_PUSH_FILE = 1,
9093 INPUT_NOFILE_OK = 2,
9094};
Eric Andersencb57d552001-06-28 07:25:16 +00009095
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009096static int plinno = 1; /* input line number */
9097/* number of characters left in input buffer */
9098static int parsenleft; /* copy of parsefile->nleft */
9099static int parselleft; /* copy of parsefile->lleft */
9100/* next character in input buffer */
9101static char *parsenextc; /* copy of parsefile->nextc */
9102
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009103static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009104/* values of checkkwd variable */
9105#define CHKALIAS 0x1
9106#define CHKKWD 0x2
9107#define CHKNL 0x4
9108
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009109static void
9110popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009111{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009112 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009113
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009114 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009115#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009116 if (sp->ap) {
9117 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9118 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009119 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009120 if (sp->string != sp->ap->val) {
9121 free(sp->string);
9122 }
9123 sp->ap->flag &= ~ALIASINUSE;
9124 if (sp->ap->flag & ALIASDEAD) {
9125 unalias(sp->ap->name);
9126 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009127 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009128#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009129 parsenextc = sp->prevstring;
9130 parsenleft = sp->prevnleft;
9131/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009132 g_parsefile->strpush = sp->prev;
9133 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009134 free(sp);
9135 INT_ON;
9136}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009137
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009138static int
9139preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009140{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009141 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009142 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009143 parsenextc = buf;
9144
Denis Vlasenko38f63192007-01-22 09:03:07 +00009145#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009146 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009147 if (!iflag || g_parsefile->fd)
9148 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009149 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009150#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009151 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009152#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009153 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9154 if (nr == 0) {
9155 /* Ctrl+C pressed */
9156 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009157 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009158 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009159 raise(SIGINT);
9160 return 1;
9161 }
Eric Andersenc470f442003-07-28 09:56:35 +00009162 goto retry;
9163 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009164 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009165 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009166 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009167 }
Eric Andersencb57d552001-06-28 07:25:16 +00009168 }
9169#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009170 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009171#endif
9172
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009173#if 0
9174/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009175 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009176 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009177 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009178 if (flags >= 0 && (flags & O_NONBLOCK)) {
9179 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009180 if (fcntl(0, F_SETFL, flags) >= 0) {
9181 out2str("sh: turning off NDELAY mode\n");
9182 goto retry;
9183 }
9184 }
9185 }
9186 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009187#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009188 return nr;
9189}
9190
9191/*
9192 * Refill the input buffer and return the next input character:
9193 *
9194 * 1) If a string was pushed back on the input, pop it;
9195 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9196 * from a string so we can't refill the buffer, return EOF.
9197 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9198 * 4) Process input up to the next newline, deleting nul characters.
9199 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009200static int
Eric Andersenc470f442003-07-28 09:56:35 +00009201preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009202{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009203 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009204 int more;
9205 char savec;
9206
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009207 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009208#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009209 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009210 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009211 return PEOA;
9212 }
Eric Andersen2870d962001-07-02 17:27:21 +00009213#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009214 popstring();
9215 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009216 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009217 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009218 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009219 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009220 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009221
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009222 more = parselleft;
9223 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009224 again:
9225 more = preadfd();
9226 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009227 parselleft = parsenleft = EOF_NLEFT;
9228 return PEOF;
9229 }
9230 }
9231
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009232 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009233
9234 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009235 for (;;) {
9236 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009237
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009238 more--;
9239 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009240
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009241 if (!c)
9242 memmove(q, q + 1, more);
9243 else {
9244 q++;
9245 if (c == '\n') {
9246 parsenleft = q - parsenextc - 1;
9247 break;
9248 }
Eric Andersencb57d552001-06-28 07:25:16 +00009249 }
9250
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009251 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009252 parsenleft = q - parsenextc - 1;
9253 if (parsenleft < 0)
9254 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009255 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009256 }
9257 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009258 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009259
9260 savec = *q;
9261 *q = '\0';
9262
9263 if (vflag) {
9264 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009265 }
9266
9267 *q = savec;
9268
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009269 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009270}
9271
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009272#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009273static int
9274pgetc(void)
9275{
9276 return pgetc_as_macro();
9277}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009278
9279#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9280#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009281#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009282#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009283#endif
9284
9285/*
9286 * Same as pgetc(), but ignores PEOA.
9287 */
9288#if ENABLE_ASH_ALIAS
9289static int
9290pgetc2(void)
9291{
9292 int c;
9293
9294 do {
9295 c = pgetc_macro();
9296 } while (c == PEOA);
9297 return c;
9298}
9299#else
9300static int
9301pgetc2(void)
9302{
9303 return pgetc_macro();
9304}
9305#endif
9306
9307/*
9308 * Read a line from the script.
9309 */
9310static char *
9311pfgets(char *line, int len)
9312{
9313 char *p = line;
9314 int nleft = len;
9315 int c;
9316
9317 while (--nleft > 0) {
9318 c = pgetc2();
9319 if (c == PEOF) {
9320 if (p == line)
9321 return NULL;
9322 break;
9323 }
9324 *p++ = c;
9325 if (c == '\n')
9326 break;
9327 }
9328 *p = '\0';
9329 return line;
9330}
9331
Eric Andersenc470f442003-07-28 09:56:35 +00009332/*
9333 * Undo the last call to pgetc. Only one character may be pushed back.
9334 * PEOF may be pushed back.
9335 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009336static void
Eric Andersenc470f442003-07-28 09:56:35 +00009337pungetc(void)
9338{
9339 parsenleft++;
9340 parsenextc--;
9341}
Eric Andersencb57d552001-06-28 07:25:16 +00009342
9343/*
9344 * Push a string back onto the input at this current parsefile level.
9345 * We handle aliases this way.
9346 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009347#if !ENABLE_ASH_ALIAS
9348#define pushstring(s, ap) pushstring(s)
9349#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009350static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009351pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009352{
Eric Andersencb57d552001-06-28 07:25:16 +00009353 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009354 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009355
Eric Andersenc470f442003-07-28 09:56:35 +00009356 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009357 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009358/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009359 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009360 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009361 sp->prev = g_parsefile->strpush;
9362 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009363 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009364 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009365 sp->prevstring = parsenextc;
9366 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009367#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009368 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009369 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009370 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009371 sp->string = s;
9372 }
Eric Andersen2870d962001-07-02 17:27:21 +00009373#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009374 parsenextc = s;
9375 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009376 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009377}
9378
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009379/*
9380 * To handle the "." command, a stack of input files is used. Pushfile
9381 * adds a new entry to the stack and popfile restores the previous level.
9382 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009383static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009384pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009385{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009386 struct parsefile *pf;
9387
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009388 g_parsefile->nleft = parsenleft;
9389 g_parsefile->lleft = parselleft;
9390 g_parsefile->nextc = parsenextc;
9391 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009392 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009393 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009394 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009395 /*pf->strpush = NULL; - ckzalloc did it */
9396 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009397 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009398}
9399
9400static void
9401popfile(void)
9402{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009403 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009404
Denis Vlasenkob012b102007-02-19 22:43:01 +00009405 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009406 if (pf->fd >= 0)
9407 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009408 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009409 while (pf->strpush)
9410 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009411 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009412 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009413 parsenleft = g_parsefile->nleft;
9414 parselleft = g_parsefile->lleft;
9415 parsenextc = g_parsefile->nextc;
9416 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009417 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009418}
9419
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009420/*
9421 * Return to top level.
9422 */
9423static void
9424popallfiles(void)
9425{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009426 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009427 popfile();
9428}
9429
9430/*
9431 * Close the file(s) that the shell is reading commands from. Called
9432 * after a fork is done.
9433 */
9434static void
9435closescript(void)
9436{
9437 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009438 if (g_parsefile->fd > 0) {
9439 close(g_parsefile->fd);
9440 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009441 }
9442}
9443
9444/*
9445 * Like setinputfile, but takes an open file descriptor. Call this with
9446 * interrupts off.
9447 */
9448static void
9449setinputfd(int fd, int push)
9450{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009451 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009452 if (push) {
9453 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009454 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009455 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009456 g_parsefile->fd = fd;
9457 if (g_parsefile->buf == NULL)
9458 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009459 parselleft = parsenleft = 0;
9460 plinno = 1;
9461}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009462
Eric Andersenc470f442003-07-28 09:56:35 +00009463/*
9464 * Set the input to take input from a file. If push is set, push the
9465 * old input onto the stack first.
9466 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009467static int
9468setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009469{
9470 int fd;
9471 int fd2;
9472
Denis Vlasenkob012b102007-02-19 22:43:01 +00009473 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009474 fd = open(fname, O_RDONLY);
9475 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009476 if (flags & INPUT_NOFILE_OK)
9477 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009478 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009479 }
Eric Andersenc470f442003-07-28 09:56:35 +00009480 if (fd < 10) {
9481 fd2 = copyfd(fd, 10);
9482 close(fd);
9483 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009484 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009485 fd = fd2;
9486 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009487 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009488 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009489 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009490 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009491}
9492
Eric Andersencb57d552001-06-28 07:25:16 +00009493/*
9494 * Like setinputfile, but takes input from a string.
9495 */
Eric Andersenc470f442003-07-28 09:56:35 +00009496static void
9497setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009498{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009499 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009500 pushfile();
9501 parsenextc = string;
9502 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009503 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009504 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009505 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009506}
9507
9508
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009509/* ============ mail.c
9510 *
9511 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009512 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009513
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009514#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009515
Eric Andersencb57d552001-06-28 07:25:16 +00009516#define MAXMBOXES 10
9517
Eric Andersenc470f442003-07-28 09:56:35 +00009518/* times of mailboxes */
9519static time_t mailtime[MAXMBOXES];
9520/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009521static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009522
Eric Andersencb57d552001-06-28 07:25:16 +00009523/*
Eric Andersenc470f442003-07-28 09:56:35 +00009524 * Print appropriate message(s) if mail has arrived.
9525 * If mail_var_path_changed is set,
9526 * then the value of MAIL has mail_var_path_changed,
9527 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009528 */
Eric Andersenc470f442003-07-28 09:56:35 +00009529static void
9530chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009531{
Eric Andersencb57d552001-06-28 07:25:16 +00009532 const char *mpath;
9533 char *p;
9534 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009535 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009536 struct stackmark smark;
9537 struct stat statb;
9538
Eric Andersencb57d552001-06-28 07:25:16 +00009539 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009540 mpath = mpathset() ? mpathval() : mailval();
9541 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009542 p = padvance(&mpath, nullstr);
9543 if (p == NULL)
9544 break;
9545 if (*p == '\0')
9546 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009547 for (q = p; *q; q++)
9548 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009549#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009550 if (q[-1] != '/')
9551 abort();
9552#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009553 q[-1] = '\0'; /* delete trailing '/' */
9554 if (stat(p, &statb) < 0) {
9555 *mtp = 0;
9556 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009557 }
Eric Andersenc470f442003-07-28 09:56:35 +00009558 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9559 fprintf(
9560 stderr, snlfmt,
9561 pathopt ? pathopt : "you have mail"
9562 );
9563 }
9564 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009565 }
Eric Andersenc470f442003-07-28 09:56:35 +00009566 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009567 popstackmark(&smark);
9568}
Eric Andersencb57d552001-06-28 07:25:16 +00009569
Eric Andersenc470f442003-07-28 09:56:35 +00009570static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009571changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009572{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009573 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009574}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009575
Denis Vlasenko131ae172007-02-18 13:00:19 +00009576#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009577
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009578
9579/* ============ ??? */
9580
Eric Andersencb57d552001-06-28 07:25:16 +00009581/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009582 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009583 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009584static void
9585setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009586{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009587 char **newparam;
9588 char **ap;
9589 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009590
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009591 for (nparam = 0; argv[nparam]; nparam++)
9592 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009593 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9594 while (*argv) {
9595 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009596 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009597 *ap = NULL;
9598 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009599 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009600 shellparam.nparam = nparam;
9601 shellparam.p = newparam;
9602#if ENABLE_ASH_GETOPTS
9603 shellparam.optind = 1;
9604 shellparam.optoff = -1;
9605#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009606}
9607
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009608/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009609 * Process shell options. The global variable argptr contains a pointer
9610 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009611 *
9612 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9613 * For a non-interactive shell, an error condition encountered
9614 * by a special built-in ... shall cause the shell to write a diagnostic message
9615 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009616 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009617 * ...
9618 * Utility syntax error (option or operand error) Shall exit
9619 * ...
9620 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9621 * we see that bash does not do that (set "finishes" with error code 1 instead,
9622 * and shell continues), and people rely on this behavior!
9623 * Testcase:
9624 * set -o barfoo 2>/dev/null
9625 * echo $?
9626 *
9627 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009628 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009629static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009630plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009631{
9632 int i;
9633
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009634 if (name) {
9635 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009636 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009637 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009638 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009639 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009640 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009641 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009642 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009643 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009644 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009645 if (val) {
9646 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9647 } else {
9648 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9649 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009650 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009651 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009652}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009653static void
9654setoption(int flag, int val)
9655{
9656 int i;
9657
9658 for (i = 0; i < NOPTS; i++) {
9659 if (optletters(i) == flag) {
9660 optlist[i] = val;
9661 return;
9662 }
9663 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009664 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009665 /* NOTREACHED */
9666}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009667static int
Eric Andersenc470f442003-07-28 09:56:35 +00009668options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009669{
9670 char *p;
9671 int val;
9672 int c;
9673
9674 if (cmdline)
9675 minusc = NULL;
9676 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009677 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009678 if (c != '-' && c != '+')
9679 break;
9680 argptr++;
9681 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009682 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009683 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009684 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009685 if (!cmdline) {
9686 /* "-" means turn off -x and -v */
9687 if (p[0] == '\0')
9688 xflag = vflag = 0;
9689 /* "--" means reset params */
9690 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009691 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009692 }
Eric Andersenc470f442003-07-28 09:56:35 +00009693 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009694 }
Eric Andersencb57d552001-06-28 07:25:16 +00009695 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009696 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009697 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009698 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009699 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009700 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009701 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009702 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009703 /* it already printed err message */
9704 return 1; /* error */
9705 }
Eric Andersencb57d552001-06-28 07:25:16 +00009706 if (*argptr)
9707 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009708 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9709 isloginsh = 1;
9710 /* bash does not accept +-login, we also won't */
9711 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009712 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009713 isloginsh = 1;
9714 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009715 } else {
9716 setoption(c, val);
9717 }
9718 }
9719 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009720 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009721}
9722
Eric Andersencb57d552001-06-28 07:25:16 +00009723/*
Eric Andersencb57d552001-06-28 07:25:16 +00009724 * The shift builtin command.
9725 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009726static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009727shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009728{
9729 int n;
9730 char **ap1, **ap2;
9731
9732 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009733 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009734 n = number(argv[1]);
9735 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009736 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009737 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009738 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009739 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009740 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009741 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009742 }
9743 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009744 while ((*ap2++ = *ap1++) != NULL)
9745 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009746#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009747 shellparam.optind = 1;
9748 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009749#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009750 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009751 return 0;
9752}
9753
Eric Andersencb57d552001-06-28 07:25:16 +00009754/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009755 * POSIX requires that 'set' (but not export or readonly) output the
9756 * variables in lexicographic order - by the locale's collating order (sigh).
9757 * Maybe we could keep them in an ordered balanced binary tree
9758 * instead of hashed lists.
9759 * For now just roll 'em through qsort for printing...
9760 */
9761static int
9762showvars(const char *sep_prefix, int on, int off)
9763{
9764 const char *sep;
9765 char **ep, **epend;
9766
9767 ep = listvars(on, off, &epend);
9768 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9769
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009770 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009771
9772 for (; ep < epend; ep++) {
9773 const char *p;
9774 const char *q;
9775
9776 p = strchrnul(*ep, '=');
9777 q = nullstr;
9778 if (*p)
9779 q = single_quote(++p);
9780 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9781 }
9782 return 0;
9783}
9784
9785/*
Eric Andersencb57d552001-06-28 07:25:16 +00009786 * The set command builtin.
9787 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009788static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009789setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009790{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009791 int retval;
9792
Denis Vlasenko68404f12008-03-17 09:00:54 +00009793 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009794 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009795 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009796 retval = 1;
9797 if (!options(0)) { /* if no parse error... */
9798 retval = 0;
9799 optschanged();
9800 if (*argptr != NULL) {
9801 setparam(argptr);
9802 }
Eric Andersencb57d552001-06-28 07:25:16 +00009803 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009804 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009805 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009806}
9807
Denis Vlasenko131ae172007-02-18 13:00:19 +00009808#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009809static void
9810change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009811{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009812 /* Galois LFSR parameter */
9813 /* Taps at 32 31 29 1: */
9814 enum { MASK = 0x8000000b };
9815 /* Another example - taps at 32 31 30 10: */
9816 /* MASK = 0x00400007 */
9817
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009818 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009819 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009820 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009821
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009822 /* LCG has period of 2^32 and alternating lowest bit */
9823 random_LCG = 1664525 * random_LCG + 1013904223;
9824 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9825 t = (random_galois_LFSR << 1);
9826 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9827 t ^= MASK;
9828 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009829 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009830 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009831 * for $RANDOM range. Combining with subtraction is
9832 * just for fun. + and ^ would work equally well. */
9833 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009834 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009835 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009836 vrandom.flags &= ~VNOFUNC;
9837 } else {
9838 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009839 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009840 }
Eric Andersenef02f822004-03-11 13:34:24 +00009841}
Eric Andersen16767e22004-03-16 05:14:10 +00009842#endif
9843
Denis Vlasenko131ae172007-02-18 13:00:19 +00009844#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009845static int
Eric Andersenc470f442003-07-28 09:56:35 +00009846getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009847{
9848 char *p, *q;
9849 char c = '?';
9850 int done = 0;
9851 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009852 char s[12];
9853 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009854
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009855 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009856 return 1;
9857 optnext = optfirst + *param_optind - 1;
9858
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009859 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009860 p = NULL;
9861 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009862 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009863 if (p == NULL || *p == '\0') {
9864 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009865 p = *optnext;
9866 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009867 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009868 p = NULL;
9869 done = 1;
9870 goto out;
9871 }
9872 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009873 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009874 goto atend;
9875 }
9876
9877 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009878 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009879 if (*q == '\0') {
9880 if (optstr[0] == ':') {
9881 s[0] = c;
9882 s[1] = '\0';
9883 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009884 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009885 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009886 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009887 }
9888 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009889 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009890 }
9891 if (*++q == ':')
9892 q++;
9893 }
9894
9895 if (*++q == ':') {
9896 if (*p == '\0' && (p = *optnext) == NULL) {
9897 if (optstr[0] == ':') {
9898 s[0] = c;
9899 s[1] = '\0';
9900 err |= setvarsafe("OPTARG", s, 0);
9901 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009902 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009903 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009904 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009905 c = '?';
9906 }
Eric Andersenc470f442003-07-28 09:56:35 +00009907 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009908 }
9909
9910 if (p == *optnext)
9911 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009912 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009913 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009914 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009915 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009916 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009917 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009918 *param_optind = optnext - optfirst + 1;
9919 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009920 err |= setvarsafe("OPTIND", s, VNOFUNC);
9921 s[0] = c;
9922 s[1] = '\0';
9923 err |= setvarsafe(optvar, s, 0);
9924 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009925 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009926 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009927 flush_stdout_stderr();
9928 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009929 }
9930 return done;
9931}
Eric Andersenc470f442003-07-28 09:56:35 +00009932
9933/*
9934 * The getopts builtin. Shellparam.optnext points to the next argument
9935 * to be processed. Shellparam.optptr points to the next character to
9936 * be processed in the current argument. If shellparam.optnext is NULL,
9937 * then it's the first time getopts has been called.
9938 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009939static int
Eric Andersenc470f442003-07-28 09:56:35 +00009940getoptscmd(int argc, char **argv)
9941{
9942 char **optbase;
9943
9944 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009945 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009946 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009947 optbase = shellparam.p;
9948 if (shellparam.optind > shellparam.nparam + 1) {
9949 shellparam.optind = 1;
9950 shellparam.optoff = -1;
9951 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009952 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009953 optbase = &argv[3];
9954 if (shellparam.optind > argc - 2) {
9955 shellparam.optind = 1;
9956 shellparam.optoff = -1;
9957 }
9958 }
9959
9960 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009961 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009962}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009963#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009964
Eric Andersencb57d552001-06-28 07:25:16 +00009965
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009966/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009967
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009968struct heredoc {
9969 struct heredoc *next; /* next here document in list */
9970 union node *here; /* redirection node */
9971 char *eofmark; /* string indicating end of input */
9972 smallint striptabs; /* if set, strip leading tabs */
9973};
9974
9975static smallint tokpushback; /* last token pushed back */
9976static smallint parsebackquote; /* nonzero if we are inside backquotes */
9977static smallint quoteflag; /* set if (part of) last token was quoted */
9978static token_id_t lasttoken; /* last token read (integer id Txxx) */
9979static struct heredoc *heredoclist; /* list of here documents to read */
9980static char *wordtext; /* text of last word returned by readtoken */
9981static struct nodelist *backquotelist;
9982static union node *redirnode;
9983static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009984/*
9985 * NEOF is returned by parsecmd when it encounters an end of file. It
9986 * must be distinct from NULL, so we use the address of a variable that
9987 * happens to be handy.
9988 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009989#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009990
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009991static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009992static void
9993raise_error_syntax(const char *msg)
9994{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009995 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009996 /* NOTREACHED */
9997}
9998
9999/*
10000 * Called when an unexpected token is read during the parse. The argument
10001 * is the token that is expected, or -1 if more than one type of token can
10002 * occur at this point.
10003 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010004static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010005static void
10006raise_error_unexpected_syntax(int token)
10007{
10008 char msg[64];
10009 int l;
10010
10011 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10012 if (token >= 0)
10013 sprintf(msg + l, " (expecting %s)", tokname(token));
10014 raise_error_syntax(msg);
10015 /* NOTREACHED */
10016}
Eric Andersencb57d552001-06-28 07:25:16 +000010017
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010018#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010019
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010020/* parsing is heavily cross-recursive, need these forward decls */
10021static union node *andor(void);
10022static union node *pipeline(void);
10023static union node *parse_command(void);
10024static void parseheredoc(void);
10025static char peektoken(void);
10026static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010027
Eric Andersenc470f442003-07-28 09:56:35 +000010028static union node *
10029list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010030{
10031 union node *n1, *n2, *n3;
10032 int tok;
10033
Eric Andersenc470f442003-07-28 09:56:35 +000010034 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10035 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010036 return NULL;
10037 n1 = NULL;
10038 for (;;) {
10039 n2 = andor();
10040 tok = readtoken();
10041 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010042 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010043 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010044 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010045 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010046 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010047 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010048 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010049 n2 = n3;
10050 }
10051 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010052 }
10053 }
10054 if (n1 == NULL) {
10055 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010056 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010057 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010058 n3->type = NSEMI;
10059 n3->nbinary.ch1 = n1;
10060 n3->nbinary.ch2 = n2;
10061 n1 = n3;
10062 }
10063 switch (tok) {
10064 case TBACKGND:
10065 case TSEMI:
10066 tok = readtoken();
10067 /* fall through */
10068 case TNL:
10069 if (tok == TNL) {
10070 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010071 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010072 return n1;
10073 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010074 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010075 }
Eric Andersenc470f442003-07-28 09:56:35 +000010076 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010077 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010078 return n1;
10079 break;
10080 case TEOF:
10081 if (heredoclist)
10082 parseheredoc();
10083 else
Eric Andersenc470f442003-07-28 09:56:35 +000010084 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010085 return n1;
10086 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010087 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010088 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010089 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010090 return n1;
10091 }
10092 }
10093}
10094
Eric Andersenc470f442003-07-28 09:56:35 +000010095static union node *
10096andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010097{
Eric Andersencb57d552001-06-28 07:25:16 +000010098 union node *n1, *n2, *n3;
10099 int t;
10100
Eric Andersencb57d552001-06-28 07:25:16 +000010101 n1 = pipeline();
10102 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010103 t = readtoken();
10104 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010105 t = NAND;
10106 } else if (t == TOR) {
10107 t = NOR;
10108 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010109 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010110 return n1;
10111 }
Eric Andersenc470f442003-07-28 09:56:35 +000010112 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010113 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010114 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010115 n3->type = t;
10116 n3->nbinary.ch1 = n1;
10117 n3->nbinary.ch2 = n2;
10118 n1 = n3;
10119 }
10120}
10121
Eric Andersenc470f442003-07-28 09:56:35 +000010122static union node *
10123pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010124{
Eric Andersencb57d552001-06-28 07:25:16 +000010125 union node *n1, *n2, *pipenode;
10126 struct nodelist *lp, *prev;
10127 int negate;
10128
10129 negate = 0;
10130 TRACE(("pipeline: entered\n"));
10131 if (readtoken() == TNOT) {
10132 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010133 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010134 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010135 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010136 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010137 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010138 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010139 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010140 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010141 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010142 pipenode->npipe.cmdlist = lp;
10143 lp->n = n1;
10144 do {
10145 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010146 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010147 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010148 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010149 prev->next = lp;
10150 } while (readtoken() == TPIPE);
10151 lp->next = NULL;
10152 n1 = pipenode;
10153 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010154 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010155 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010156 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010157 n2->type = NNOT;
10158 n2->nnot.com = n1;
10159 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010160 }
10161 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010162}
10163
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010164static union node *
10165makename(void)
10166{
10167 union node *n;
10168
Denis Vlasenko597906c2008-02-20 16:38:54 +000010169 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010170 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010171 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010172 n->narg.text = wordtext;
10173 n->narg.backquote = backquotelist;
10174 return n;
10175}
10176
10177static void
10178fixredir(union node *n, const char *text, int err)
10179{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010180 int fd;
10181
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010182 TRACE(("Fix redir %s %d\n", text, err));
10183 if (!err)
10184 n->ndup.vname = NULL;
10185
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010186 fd = bb_strtou(text, NULL, 10);
10187 if (!errno && fd >= 0)
10188 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010189 else if (LONE_DASH(text))
10190 n->ndup.dupfd = -1;
10191 else {
10192 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010193 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010194 n->ndup.vname = makename();
10195 }
10196}
10197
10198/*
10199 * Returns true if the text contains nothing to expand (no dollar signs
10200 * or backquotes).
10201 */
10202static int
10203noexpand(char *text)
10204{
10205 char *p;
10206 char c;
10207
10208 p = text;
10209 while ((c = *p++) != '\0') {
10210 if (c == CTLQUOTEMARK)
10211 continue;
10212 if (c == CTLESC)
10213 p++;
10214 else if (SIT(c, BASESYNTAX) == CCTL)
10215 return 0;
10216 }
10217 return 1;
10218}
10219
10220static void
10221parsefname(void)
10222{
10223 union node *n = redirnode;
10224
10225 if (readtoken() != TWORD)
10226 raise_error_unexpected_syntax(-1);
10227 if (n->type == NHERE) {
10228 struct heredoc *here = heredoc;
10229 struct heredoc *p;
10230 int i;
10231
10232 if (quoteflag == 0)
10233 n->type = NXHERE;
10234 TRACE(("Here document %d\n", n->type));
10235 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010236 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010237 rmescapes(wordtext);
10238 here->eofmark = wordtext;
10239 here->next = NULL;
10240 if (heredoclist == NULL)
10241 heredoclist = here;
10242 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010243 for (p = heredoclist; p->next; p = p->next)
10244 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010245 p->next = here;
10246 }
10247 } else if (n->type == NTOFD || n->type == NFROMFD) {
10248 fixredir(n, wordtext, 0);
10249 } else {
10250 n->nfile.fname = makename();
10251 }
10252}
Eric Andersencb57d552001-06-28 07:25:16 +000010253
Eric Andersenc470f442003-07-28 09:56:35 +000010254static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010255simplecmd(void)
10256{
10257 union node *args, **app;
10258 union node *n = NULL;
10259 union node *vars, **vpp;
10260 union node **rpp, *redir;
10261 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010262#if ENABLE_ASH_BASH_COMPAT
10263 smallint double_brackets_flag = 0;
10264#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010265
10266 args = NULL;
10267 app = &args;
10268 vars = NULL;
10269 vpp = &vars;
10270 redir = NULL;
10271 rpp = &redir;
10272
10273 savecheckkwd = CHKALIAS;
10274 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010275 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010276 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010277 t = readtoken();
10278 switch (t) {
10279#if ENABLE_ASH_BASH_COMPAT
10280 case TAND: /* "&&" */
10281 case TOR: /* "||" */
10282 if (!double_brackets_flag) {
10283 tokpushback = 1;
10284 goto out;
10285 }
10286 wordtext = (char *) (t == TAND ? "-a" : "-o");
10287#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010288 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010289 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010290 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010291 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010292 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010293#if ENABLE_ASH_BASH_COMPAT
10294 if (strcmp("[[", wordtext) == 0)
10295 double_brackets_flag = 1;
10296 else if (strcmp("]]", wordtext) == 0)
10297 double_brackets_flag = 0;
10298#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010299 n->narg.backquote = backquotelist;
10300 if (savecheckkwd && isassignment(wordtext)) {
10301 *vpp = n;
10302 vpp = &n->narg.next;
10303 } else {
10304 *app = n;
10305 app = &n->narg.next;
10306 savecheckkwd = 0;
10307 }
10308 break;
10309 case TREDIR:
10310 *rpp = n = redirnode;
10311 rpp = &n->nfile.next;
10312 parsefname(); /* read name of redirection file */
10313 break;
10314 case TLP:
10315 if (args && app == &args->narg.next
10316 && !vars && !redir
10317 ) {
10318 struct builtincmd *bcmd;
10319 const char *name;
10320
10321 /* We have a function */
10322 if (readtoken() != TRP)
10323 raise_error_unexpected_syntax(TRP);
10324 name = n->narg.text;
10325 if (!goodname(name)
10326 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10327 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010328 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010329 }
10330 n->type = NDEFUN;
10331 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10332 n->narg.next = parse_command();
10333 return n;
10334 }
10335 /* fall through */
10336 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010337 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010338 goto out;
10339 }
10340 }
10341 out:
10342 *app = NULL;
10343 *vpp = NULL;
10344 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010345 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010346 n->type = NCMD;
10347 n->ncmd.args = args;
10348 n->ncmd.assign = vars;
10349 n->ncmd.redirect = redir;
10350 return n;
10351}
10352
10353static union node *
10354parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010355{
Eric Andersencb57d552001-06-28 07:25:16 +000010356 union node *n1, *n2;
10357 union node *ap, **app;
10358 union node *cp, **cpp;
10359 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010360 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010361 int t;
10362
10363 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010364 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010365
Eric Andersencb57d552001-06-28 07:25:16 +000010366 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010367 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010368 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010369 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010370 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010371 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010372 n1->type = NIF;
10373 n1->nif.test = list(0);
10374 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010375 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010376 n1->nif.ifpart = list(0);
10377 n2 = n1;
10378 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010379 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010380 n2 = n2->nif.elsepart;
10381 n2->type = NIF;
10382 n2->nif.test = list(0);
10383 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010384 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010385 n2->nif.ifpart = list(0);
10386 }
10387 if (lasttoken == TELSE)
10388 n2->nif.elsepart = list(0);
10389 else {
10390 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010391 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010392 }
Eric Andersenc470f442003-07-28 09:56:35 +000010393 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010394 break;
10395 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010396 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010397 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010398 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010399 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010400 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010401 got = readtoken();
10402 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010403 TRACE(("expecting DO got %s %s\n", tokname(got),
10404 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010405 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010406 }
10407 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010408 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010409 break;
10410 }
10411 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010412 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010413 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010414 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010415 n1->type = NFOR;
10416 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010417 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010418 if (readtoken() == TIN) {
10419 app = &ap;
10420 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010421 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010422 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010423 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010424 n2->narg.text = wordtext;
10425 n2->narg.backquote = backquotelist;
10426 *app = n2;
10427 app = &n2->narg.next;
10428 }
10429 *app = NULL;
10430 n1->nfor.args = ap;
10431 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010432 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010433 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010434 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010435 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010436 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010437 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010438 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010439 n1->nfor.args = n2;
10440 /*
10441 * Newline or semicolon here is optional (but note
10442 * that the original Bourne shell only allowed NL).
10443 */
10444 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010445 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010446 }
Eric Andersenc470f442003-07-28 09:56:35 +000010447 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010448 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010449 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010450 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010451 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010452 break;
10453 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010454 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010455 n1->type = NCASE;
10456 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010457 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010458 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010459 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010460 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010461 n2->narg.text = wordtext;
10462 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010463 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010464 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010465 } while (readtoken() == TNL);
10466 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010467 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010468 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010469 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010470 checkkwd = CHKNL | CHKKWD;
10471 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010472 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010473 if (lasttoken == TLP)
10474 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010475 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010476 cp->type = NCLIST;
10477 app = &cp->nclist.pattern;
10478 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010479 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010480 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010481 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010482 ap->narg.text = wordtext;
10483 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010484 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010485 break;
10486 app = &ap->narg.next;
10487 readtoken();
10488 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010489 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010490 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010491 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010492 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010493
Eric Andersenc470f442003-07-28 09:56:35 +000010494 cpp = &cp->nclist.next;
10495
10496 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010497 t = readtoken();
10498 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010499 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010500 raise_error_unexpected_syntax(TENDCASE);
10501 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010502 }
Eric Andersenc470f442003-07-28 09:56:35 +000010503 }
Eric Andersencb57d552001-06-28 07:25:16 +000010504 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010505 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010506 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010507 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010508 n1->type = NSUBSHELL;
10509 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010510 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010511 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010512 break;
10513 case TBEGIN:
10514 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010515 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010516 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010517 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010518 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010519 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010520 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010521 }
10522
Eric Andersenc470f442003-07-28 09:56:35 +000010523 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010524 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010525
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010526 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010527 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010528 checkkwd = CHKKWD | CHKALIAS;
10529 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010530 while (readtoken() == TREDIR) {
10531 *rpp = n2 = redirnode;
10532 rpp = &n2->nfile.next;
10533 parsefname();
10534 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010535 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010536 *rpp = NULL;
10537 if (redir) {
10538 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010539 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010540 n2->type = NREDIR;
10541 n2->nredir.n = n1;
10542 n1 = n2;
10543 }
10544 n1->nredir.redirect = redir;
10545 }
Eric Andersencb57d552001-06-28 07:25:16 +000010546 return n1;
10547}
10548
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010549#if ENABLE_ASH_BASH_COMPAT
10550static int decode_dollar_squote(void)
10551{
10552 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10553 int c, cnt;
10554 char *p;
10555 char buf[4];
10556
10557 c = pgetc();
10558 p = strchr(C_escapes, c);
10559 if (p) {
10560 buf[0] = c;
10561 p = buf;
10562 cnt = 3;
10563 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10564 do {
10565 c = pgetc();
10566 *++p = c;
10567 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10568 pungetc();
10569 } else if (c == 'x') { /* \xHH */
10570 do {
10571 c = pgetc();
10572 *++p = c;
10573 } while (isxdigit(c) && --cnt);
10574 pungetc();
10575 if (cnt == 3) { /* \x but next char is "bad" */
10576 c = 'x';
10577 goto unrecognized;
10578 }
10579 } else { /* simple seq like \\ or \t */
10580 p++;
10581 }
10582 *p = '\0';
10583 p = buf;
10584 c = bb_process_escape_sequence((void*)&p);
10585 } else { /* unrecognized "\z": print both chars unless ' or " */
10586 if (c != '\'' && c != '"') {
10587 unrecognized:
10588 c |= 0x100; /* "please encode \, then me" */
10589 }
10590 }
10591 return c;
10592}
10593#endif
10594
Eric Andersencb57d552001-06-28 07:25:16 +000010595/*
10596 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10597 * is not NULL, read a here document. In the latter case, eofmark is the
10598 * word which marks the end of the document and striptabs is true if
10599 * leading tabs should be stripped from the document. The argument firstc
10600 * is the first character of the input token or document.
10601 *
10602 * Because C does not have internal subroutines, I have simulated them
10603 * using goto's to implement the subroutine linkage. The following macros
10604 * will run code that appears at the end of readtoken1.
10605 */
Eric Andersen2870d962001-07-02 17:27:21 +000010606#define CHECKEND() {goto checkend; checkend_return:;}
10607#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10608#define PARSESUB() {goto parsesub; parsesub_return:;}
10609#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10610#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10611#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010612static int
Eric Andersenc470f442003-07-28 09:56:35 +000010613readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010614{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010615 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010616 int c = firstc;
10617 char *out;
10618 int len;
10619 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010620 struct nodelist *bqlist;
10621 smallint quotef;
10622 smallint dblquote;
10623 smallint oldstyle;
10624 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010625#if ENABLE_ASH_EXPAND_PRMT
10626 smallint pssyntax; /* we are expanding a prompt string */
10627#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010628 int varnest; /* levels of variables expansion */
10629 int arinest; /* levels of arithmetic expansion */
10630 int parenlevel; /* levels of parens in arithmetic */
10631 int dqvarnest; /* levels of variables expansion within double quotes */
10632
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010633 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10634
Eric Andersencb57d552001-06-28 07:25:16 +000010635#if __GNUC__
10636 /* Avoid longjmp clobbering */
10637 (void) &out;
10638 (void) &quotef;
10639 (void) &dblquote;
10640 (void) &varnest;
10641 (void) &arinest;
10642 (void) &parenlevel;
10643 (void) &dqvarnest;
10644 (void) &oldstyle;
10645 (void) &prevsyntax;
10646 (void) &syntax;
10647#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010648 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010649 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010650 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010651 oldstyle = 0;
10652 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010653#if ENABLE_ASH_EXPAND_PRMT
10654 pssyntax = (syntax == PSSYNTAX);
10655 if (pssyntax)
10656 syntax = DQSYNTAX;
10657#endif
10658 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010659 varnest = 0;
10660 arinest = 0;
10661 parenlevel = 0;
10662 dqvarnest = 0;
10663
10664 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010665 loop:
10666 /* For each line, until end of word */
10667 {
Eric Andersenc470f442003-07-28 09:56:35 +000010668 CHECKEND(); /* set c to PEOF if at end of here document */
10669 for (;;) { /* until end of line or end of word */
10670 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010671 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010672 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010673 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010674 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010675 USTPUTC(c, out);
10676 plinno++;
10677 if (doprompt)
10678 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010679 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010680 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010681 case CWORD:
10682 USTPUTC(c, out);
10683 break;
10684 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010685 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010686 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010687#if ENABLE_ASH_BASH_COMPAT
10688 if (c == '\\' && bash_dollar_squote) {
10689 c = decode_dollar_squote();
10690 if (c & 0x100) {
10691 USTPUTC('\\', out);
10692 c = (unsigned char)c;
10693 }
10694 }
10695#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010696 USTPUTC(c, out);
10697 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010698 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010699 c = pgetc2();
10700 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010701 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010702 USTPUTC('\\', out);
10703 pungetc();
10704 } else if (c == '\n') {
10705 if (doprompt)
10706 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010707 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010708#if ENABLE_ASH_EXPAND_PRMT
10709 if (c == '$' && pssyntax) {
10710 USTPUTC(CTLESC, out);
10711 USTPUTC('\\', out);
10712 }
10713#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010714 if (dblquote && c != '\\'
10715 && c != '`' && c != '$'
10716 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010717 ) {
10718 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010719 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010720 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010721 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010722 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010723 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010724 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010725 }
10726 break;
10727 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010728 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010729 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010730 if (eofmark == NULL) {
10731 USTPUTC(CTLQUOTEMARK, out);
10732 }
Eric Andersencb57d552001-06-28 07:25:16 +000010733 break;
10734 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010735 syntax = DQSYNTAX;
10736 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010737 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010738 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010739 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010740 if (eofmark != NULL && arinest == 0
10741 && varnest == 0
10742 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010743 USTPUTC(c, out);
10744 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010745 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010746 syntax = BASESYNTAX;
10747 dblquote = 0;
10748 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010749 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010750 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010751 }
10752 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010753 case CVAR: /* '$' */
10754 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010755 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010756 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010757 if (varnest > 0) {
10758 varnest--;
10759 if (dqvarnest > 0) {
10760 dqvarnest--;
10761 }
10762 USTPUTC(CTLENDVAR, out);
10763 } else {
10764 USTPUTC(c, out);
10765 }
10766 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010767#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010768 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010769 parenlevel++;
10770 USTPUTC(c, out);
10771 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010772 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010773 if (parenlevel > 0) {
10774 USTPUTC(c, out);
10775 --parenlevel;
10776 } else {
10777 if (pgetc() == ')') {
10778 if (--arinest == 0) {
10779 USTPUTC(CTLENDARI, out);
10780 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010781 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010782 } else
10783 USTPUTC(')', out);
10784 } else {
10785 /*
10786 * unbalanced parens
10787 * (don't 2nd guess - no error)
10788 */
10789 pungetc();
10790 USTPUTC(')', out);
10791 }
10792 }
10793 break;
10794#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010795 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010796 PARSEBACKQOLD();
10797 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010798 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010799 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010800 case CIGN:
10801 break;
10802 default:
10803 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010804 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010805#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010806 if (c != PEOA)
10807#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010808 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010809
Eric Andersencb57d552001-06-28 07:25:16 +000010810 }
10811 c = pgetc_macro();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010812 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010813 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010814 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010815#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010816 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010817 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010818#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010819 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010820 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010821 if (varnest != 0) {
10822 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010823 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010824 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010825 }
10826 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010827 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010828 out = stackblock();
10829 if (eofmark == NULL) {
Denis Vlasenko5a867312008-07-24 19:46:38 +000010830 if ((c == '>' || c == '<') && quotef == 0) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010831 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010832 PARSEREDIR(); /* passed as params: out, c */
10833 lasttoken = TREDIR;
10834 return lasttoken;
10835 }
10836 /* else: non-number X seen, interpret it
10837 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010838 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010839 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010840 }
10841 quoteflag = quotef;
10842 backquotelist = bqlist;
10843 grabstackblock(len);
10844 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010845 lasttoken = TWORD;
10846 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010847/* end of readtoken routine */
10848
Eric Andersencb57d552001-06-28 07:25:16 +000010849/*
10850 * Check to see whether we are at the end of the here document. When this
10851 * is called, c is set to the first character of the next input line. If
10852 * we are at the end of the here document, this routine sets the c to PEOF.
10853 */
Eric Andersenc470f442003-07-28 09:56:35 +000010854checkend: {
10855 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010856#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010857 if (c == PEOA) {
10858 c = pgetc2();
10859 }
10860#endif
10861 if (striptabs) {
10862 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010863 c = pgetc2();
10864 }
Eric Andersenc470f442003-07-28 09:56:35 +000010865 }
10866 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010867 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010868 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010869
Eric Andersenc470f442003-07-28 09:56:35 +000010870 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010871 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10872 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010873 if (*p == '\n' && *q == '\0') {
10874 c = PEOF;
10875 plinno++;
10876 needprompt = doprompt;
10877 } else {
10878 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010879 }
10880 }
10881 }
10882 }
Eric Andersenc470f442003-07-28 09:56:35 +000010883 goto checkend_return;
10884}
Eric Andersencb57d552001-06-28 07:25:16 +000010885
Eric Andersencb57d552001-06-28 07:25:16 +000010886/*
10887 * Parse a redirection operator. The variable "out" points to a string
10888 * specifying the fd to be redirected. The variable "c" contains the
10889 * first character of the redirection operator.
10890 */
Eric Andersenc470f442003-07-28 09:56:35 +000010891parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010892 /* out is already checked to be a valid number or "" */
10893 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010894 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010895
Denis Vlasenko597906c2008-02-20 16:38:54 +000010896 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010897 if (c == '>') {
10898 np->nfile.fd = 1;
10899 c = pgetc();
10900 if (c == '>')
10901 np->type = NAPPEND;
10902 else if (c == '|')
10903 np->type = NCLOBBER;
10904 else if (c == '&')
10905 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010906 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010907 else {
10908 np->type = NTO;
10909 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010910 }
Eric Andersenc470f442003-07-28 09:56:35 +000010911 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010912 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010913 c = pgetc();
10914 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010915 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010916 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010917 np = stzalloc(sizeof(struct nhere));
10918 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010919 }
10920 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010921 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010922 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010923 c = pgetc();
10924 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010925 heredoc->striptabs = 1;
10926 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010927 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010928 pungetc();
10929 }
10930 break;
10931
10932 case '&':
10933 np->type = NFROMFD;
10934 break;
10935
10936 case '>':
10937 np->type = NFROMTO;
10938 break;
10939
10940 default:
10941 np->type = NFROM;
10942 pungetc();
10943 break;
10944 }
Eric Andersencb57d552001-06-28 07:25:16 +000010945 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010946 if (fd >= 0)
10947 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010948 redirnode = np;
10949 goto parseredir_return;
10950}
Eric Andersencb57d552001-06-28 07:25:16 +000010951
Eric Andersencb57d552001-06-28 07:25:16 +000010952/*
10953 * Parse a substitution. At this point, we have read the dollar sign
10954 * and nothing else.
10955 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010956
10957/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10958 * (assuming ascii char codes, as the original implementation did) */
10959#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010960 (((unsigned)(c) - 33 < 32) \
10961 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010962parsesub: {
10963 int subtype;
10964 int typeloc;
10965 int flags;
10966 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010967 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010968
Eric Andersenc470f442003-07-28 09:56:35 +000010969 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010970 if (c <= PEOA_OR_PEOF
10971 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010972 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010973#if ENABLE_ASH_BASH_COMPAT
10974 if (c == '\'')
10975 bash_dollar_squote = 1;
10976 else
10977#endif
10978 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010979 pungetc();
10980 } else if (c == '(') { /* $(command) or $((arith)) */
10981 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010982#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010983 PARSEARITH();
10984#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000010985 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000010986#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010987 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010988 pungetc();
10989 PARSEBACKQNEW();
10990 }
10991 } else {
10992 USTPUTC(CTLVAR, out);
10993 typeloc = out - (char *)stackblock();
10994 USTPUTC(VSNORMAL, out);
10995 subtype = VSNORMAL;
10996 if (c == '{') {
10997 c = pgetc();
10998 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010999 c = pgetc();
11000 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011001 c = '#';
11002 else
11003 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011004 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011005 subtype = 0;
11006 }
11007 if (c > PEOA_OR_PEOF && is_name(c)) {
11008 do {
11009 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011010 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011011 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011012 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011013 do {
11014 STPUTC(c, out);
11015 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011016 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011017 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011018 USTPUTC(c, out);
11019 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011020 } else {
11021 badsub:
11022 raise_error_syntax("bad substitution");
11023 }
Eric Andersencb57d552001-06-28 07:25:16 +000011024
Eric Andersenc470f442003-07-28 09:56:35 +000011025 STPUTC('=', out);
11026 flags = 0;
11027 if (subtype == 0) {
11028 switch (c) {
11029 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011030 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011031#if ENABLE_ASH_BASH_COMPAT
11032 if (c == ':' || c == '$' || isdigit(c)) {
11033 pungetc();
11034 subtype = VSSUBSTR;
11035 break;
11036 }
11037#endif
11038 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011039 /*FALLTHROUGH*/
11040 default:
11041 p = strchr(types, c);
11042 if (p == NULL)
11043 goto badsub;
11044 subtype = p - types + VSNORMAL;
11045 break;
11046 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011047 case '#': {
11048 int cc = c;
11049 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11050 c = pgetc();
11051 if (c == cc)
11052 subtype++;
11053 else
11054 pungetc();
11055 break;
11056 }
11057#if ENABLE_ASH_BASH_COMPAT
11058 case '/':
11059 subtype = VSREPLACE;
11060 c = pgetc();
11061 if (c == '/')
11062 subtype++; /* VSREPLACEALL */
11063 else
11064 pungetc();
11065 break;
11066#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011067 }
Eric Andersenc470f442003-07-28 09:56:35 +000011068 } else {
11069 pungetc();
11070 }
11071 if (dblquote || arinest)
11072 flags |= VSQUOTE;
11073 *((char *)stackblock() + typeloc) = subtype | flags;
11074 if (subtype != VSNORMAL) {
11075 varnest++;
11076 if (dblquote || arinest) {
11077 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011078 }
11079 }
11080 }
Eric Andersenc470f442003-07-28 09:56:35 +000011081 goto parsesub_return;
11082}
Eric Andersencb57d552001-06-28 07:25:16 +000011083
Eric Andersencb57d552001-06-28 07:25:16 +000011084/*
11085 * Called to parse command substitutions. Newstyle is set if the command
11086 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11087 * list of commands (passed by reference), and savelen is the number of
11088 * characters on the top of the stack which must be preserved.
11089 */
Eric Andersenc470f442003-07-28 09:56:35 +000011090parsebackq: {
11091 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011092 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011093 union node *n;
11094 char *volatile str;
11095 struct jmploc jmploc;
11096 struct jmploc *volatile savehandler;
11097 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011098 smallint saveprompt = 0;
11099
Eric Andersencb57d552001-06-28 07:25:16 +000011100#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011101 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011102#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011103 savepbq = parsebackquote;
11104 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011105 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011106 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011107 exception_handler = savehandler;
11108 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011109 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011110 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011111 str = NULL;
11112 savelen = out - (char *)stackblock();
11113 if (savelen > 0) {
11114 str = ckmalloc(savelen);
11115 memcpy(str, stackblock(), savelen);
11116 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011117 savehandler = exception_handler;
11118 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011119 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011120 if (oldstyle) {
11121 /* We must read until the closing backquote, giving special
11122 treatment to some slashes, and then push the string and
11123 reread it as input, interpreting it normally. */
11124 char *pout;
11125 int pc;
11126 size_t psavelen;
11127 char *pstr;
11128
11129
11130 STARTSTACKSTR(pout);
11131 for (;;) {
11132 if (needprompt) {
11133 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011134 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011135 pc = pgetc();
11136 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011137 case '`':
11138 goto done;
11139
11140 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011141 pc = pgetc();
11142 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000011143 plinno++;
11144 if (doprompt)
11145 setprompt(2);
11146 /*
11147 * If eating a newline, avoid putting
11148 * the newline into the new character
11149 * stream (via the STPUTC after the
11150 * switch).
11151 */
11152 continue;
11153 }
11154 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011155 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011156 STPUTC('\\', pout);
11157 if (pc > PEOA_OR_PEOF) {
11158 break;
11159 }
11160 /* fall through */
11161
11162 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011163#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011164 case PEOA:
11165#endif
11166 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011167 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011168
11169 case '\n':
11170 plinno++;
11171 needprompt = doprompt;
11172 break;
11173
11174 default:
11175 break;
11176 }
11177 STPUTC(pc, pout);
11178 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011179 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011180 STPUTC('\0', pout);
11181 psavelen = pout - (char *)stackblock();
11182 if (psavelen > 0) {
11183 pstr = grabstackstr(pout);
11184 setinputstring(pstr);
11185 }
11186 }
11187 nlpp = &bqlist;
11188 while (*nlpp)
11189 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011190 *nlpp = stzalloc(sizeof(**nlpp));
11191 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011192 parsebackquote = oldstyle;
11193
11194 if (oldstyle) {
11195 saveprompt = doprompt;
11196 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011197 }
11198
Eric Andersenc470f442003-07-28 09:56:35 +000011199 n = list(2);
11200
11201 if (oldstyle)
11202 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011203 else if (readtoken() != TRP)
11204 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011205
11206 (*nlpp)->n = n;
11207 if (oldstyle) {
11208 /*
11209 * Start reading from old file again, ignoring any pushed back
11210 * tokens left from the backquote parsing
11211 */
11212 popfile();
11213 tokpushback = 0;
11214 }
11215 while (stackblocksize() <= savelen)
11216 growstackblock();
11217 STARTSTACKSTR(out);
11218 if (str) {
11219 memcpy(out, str, savelen);
11220 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011221 INT_OFF;
11222 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011223 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011224 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011225 }
11226 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011227 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011228 if (arinest || dblquote)
11229 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11230 else
11231 USTPUTC(CTLBACKQ, out);
11232 if (oldstyle)
11233 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011234 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011235}
11236
Denis Vlasenko131ae172007-02-18 13:00:19 +000011237#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011238/*
11239 * Parse an arithmetic expansion (indicate start of one and set state)
11240 */
Eric Andersenc470f442003-07-28 09:56:35 +000011241parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011242 if (++arinest == 1) {
11243 prevsyntax = syntax;
11244 syntax = ARISYNTAX;
11245 USTPUTC(CTLARI, out);
11246 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011247 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011248 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011249 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011250 } else {
11251 /*
11252 * we collapse embedded arithmetic expansion to
11253 * parenthesis, which should be equivalent
11254 */
11255 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011256 }
Eric Andersenc470f442003-07-28 09:56:35 +000011257 goto parsearith_return;
11258}
11259#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011260
Eric Andersenc470f442003-07-28 09:56:35 +000011261} /* end of readtoken */
11262
Eric Andersencb57d552001-06-28 07:25:16 +000011263/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011264 * Read the next input token.
11265 * If the token is a word, we set backquotelist to the list of cmds in
11266 * backquotes. We set quoteflag to true if any part of the word was
11267 * quoted.
11268 * If the token is TREDIR, then we set redirnode to a structure containing
11269 * the redirection.
11270 * In all cases, the variable startlinno is set to the number of the line
11271 * on which the token starts.
11272 *
11273 * [Change comment: here documents and internal procedures]
11274 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11275 * word parsing code into a separate routine. In this case, readtoken
11276 * doesn't need to have any internal procedures, but parseword does.
11277 * We could also make parseoperator in essence the main routine, and
11278 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011279 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011280#define NEW_xxreadtoken
11281#ifdef NEW_xxreadtoken
11282/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011283static const char xxreadtoken_chars[7] ALIGN1 = {
11284 '\n', '(', ')', '&', '|', ';', 0
11285};
Eric Andersencb57d552001-06-28 07:25:16 +000011286
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011287static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011288 TNL, TLP, TRP, /* only single occurrence allowed */
11289 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11290 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011291 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011292};
11293
11294#define xxreadtoken_doubles \
11295 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11296#define xxreadtoken_singles \
11297 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11298
11299static int
11300xxreadtoken(void)
11301{
11302 int c;
11303
11304 if (tokpushback) {
11305 tokpushback = 0;
11306 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011307 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011308 if (needprompt) {
11309 setprompt(2);
11310 }
11311 startlinno = plinno;
11312 for (;;) { /* until token or start of word found */
11313 c = pgetc_macro();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011314 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11315 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011316
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011317 if (c == '#') {
11318 while ((c = pgetc()) != '\n' && c != PEOF)
11319 continue;
11320 pungetc();
11321 } else if (c == '\\') {
11322 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011323 pungetc();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011324 goto READTOKEN1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011325 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011326 startlinno = ++plinno;
11327 if (doprompt)
11328 setprompt(2);
11329 } else {
11330 const char *p;
11331
11332 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11333 if (c != PEOF) {
11334 if (c == '\n') {
11335 plinno++;
11336 needprompt = doprompt;
11337 }
11338
11339 p = strchr(xxreadtoken_chars, c);
11340 if (p == NULL) {
11341 READTOKEN1:
11342 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11343 }
11344
11345 if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11346 if (pgetc() == *p) { /* double occurrence? */
11347 p += xxreadtoken_doubles + 1;
11348 } else {
11349 pungetc();
11350 }
11351 }
11352 }
11353 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11354 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011355 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011356 } /* for (;;) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011357}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011358#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011359#define RETURN(token) return lasttoken = token
11360static int
11361xxreadtoken(void)
11362{
11363 int c;
11364
11365 if (tokpushback) {
11366 tokpushback = 0;
11367 return lasttoken;
11368 }
11369 if (needprompt) {
11370 setprompt(2);
11371 }
11372 startlinno = plinno;
11373 for (;;) { /* until token or start of word found */
11374 c = pgetc_macro();
11375 switch (c) {
11376 case ' ': case '\t':
11377#if ENABLE_ASH_ALIAS
11378 case PEOA:
11379#endif
11380 continue;
11381 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011382 while ((c = pgetc()) != '\n' && c != PEOF)
11383 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011384 pungetc();
11385 continue;
11386 case '\\':
11387 if (pgetc() == '\n') {
11388 startlinno = ++plinno;
11389 if (doprompt)
11390 setprompt(2);
11391 continue;
11392 }
11393 pungetc();
11394 goto breakloop;
11395 case '\n':
11396 plinno++;
11397 needprompt = doprompt;
11398 RETURN(TNL);
11399 case PEOF:
11400 RETURN(TEOF);
11401 case '&':
11402 if (pgetc() == '&')
11403 RETURN(TAND);
11404 pungetc();
11405 RETURN(TBACKGND);
11406 case '|':
11407 if (pgetc() == '|')
11408 RETURN(TOR);
11409 pungetc();
11410 RETURN(TPIPE);
11411 case ';':
11412 if (pgetc() == ';')
11413 RETURN(TENDCASE);
11414 pungetc();
11415 RETURN(TSEMI);
11416 case '(':
11417 RETURN(TLP);
11418 case ')':
11419 RETURN(TRP);
11420 default:
11421 goto breakloop;
11422 }
11423 }
11424 breakloop:
11425 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11426#undef RETURN
11427}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011428#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011429
11430static int
11431readtoken(void)
11432{
11433 int t;
11434#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011435 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011436#endif
11437
11438#if ENABLE_ASH_ALIAS
11439 top:
11440#endif
11441
11442 t = xxreadtoken();
11443
11444 /*
11445 * eat newlines
11446 */
11447 if (checkkwd & CHKNL) {
11448 while (t == TNL) {
11449 parseheredoc();
11450 t = xxreadtoken();
11451 }
11452 }
11453
11454 if (t != TWORD || quoteflag) {
11455 goto out;
11456 }
11457
11458 /*
11459 * check for keywords
11460 */
11461 if (checkkwd & CHKKWD) {
11462 const char *const *pp;
11463
11464 pp = findkwd(wordtext);
11465 if (pp) {
11466 lasttoken = t = pp - tokname_array;
11467 TRACE(("keyword %s recognized\n", tokname(t)));
11468 goto out;
11469 }
11470 }
11471
11472 if (checkkwd & CHKALIAS) {
11473#if ENABLE_ASH_ALIAS
11474 struct alias *ap;
11475 ap = lookupalias(wordtext, 1);
11476 if (ap != NULL) {
11477 if (*ap->val) {
11478 pushstring(ap->val, ap);
11479 }
11480 goto top;
11481 }
11482#endif
11483 }
11484 out:
11485 checkkwd = 0;
11486#if DEBUG
11487 if (!alreadyseen)
11488 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11489 else
11490 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11491#endif
11492 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011493}
11494
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011495static char
11496peektoken(void)
11497{
11498 int t;
11499
11500 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011501 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011502 return tokname_array[t][0];
11503}
Eric Andersencb57d552001-06-28 07:25:16 +000011504
11505/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011506 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11507 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011508 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011509static union node *
11510parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011511{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011512 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011513
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011514 tokpushback = 0;
11515 doprompt = interact;
11516 if (doprompt)
11517 setprompt(doprompt);
11518 needprompt = 0;
11519 t = readtoken();
11520 if (t == TEOF)
11521 return NEOF;
11522 if (t == TNL)
11523 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011524 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011525 return list(1);
11526}
11527
11528/*
11529 * Input any here documents.
11530 */
11531static void
11532parseheredoc(void)
11533{
11534 struct heredoc *here;
11535 union node *n;
11536
11537 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011538 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011539
11540 while (here) {
11541 if (needprompt) {
11542 setprompt(2);
11543 }
11544 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11545 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011546 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011547 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011548 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011549 n->narg.text = wordtext;
11550 n->narg.backquote = backquotelist;
11551 here->here->nhere.doc = n;
11552 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011553 }
Eric Andersencb57d552001-06-28 07:25:16 +000011554}
11555
11556
11557/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011558 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011559 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011560#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011561static const char *
11562expandstr(const char *ps)
11563{
11564 union node n;
11565
11566 /* XXX Fix (char *) cast. */
11567 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011568 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011569 popfile();
11570
11571 n.narg.type = NARG;
11572 n.narg.next = NULL;
11573 n.narg.text = wordtext;
11574 n.narg.backquote = backquotelist;
11575
11576 expandarg(&n, NULL, 0);
11577 return stackblock();
11578}
11579#endif
11580
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011581/*
11582 * Execute a command or commands contained in a string.
11583 */
11584static int
11585evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011586{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011587 union node *n;
11588 struct stackmark smark;
11589 int skip;
11590
11591 setinputstring(s);
11592 setstackmark(&smark);
11593
11594 skip = 0;
11595 while ((n = parsecmd(0)) != NEOF) {
11596 evaltree(n, 0);
11597 popstackmark(&smark);
11598 skip = evalskip;
11599 if (skip)
11600 break;
11601 }
11602 popfile();
11603
11604 skip &= mask;
11605 evalskip = skip;
11606 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011607}
11608
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011609/*
11610 * The eval command.
11611 */
11612static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011613evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011614{
11615 char *p;
11616 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011617
Denis Vlasenko68404f12008-03-17 09:00:54 +000011618 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011619 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011620 argv += 2;
11621 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011622 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011623 for (;;) {
11624 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011625 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011626 if (p == NULL)
11627 break;
11628 STPUTC(' ', concat);
11629 }
11630 STPUTC('\0', concat);
11631 p = grabstackstr(concat);
11632 }
11633 evalstring(p, ~SKIPEVAL);
11634
11635 }
11636 return exitstatus;
11637}
11638
11639/*
11640 * Read and execute commands. "Top" is nonzero for the top level command
11641 * loop; it turns on prompting if the shell is interactive.
11642 */
11643static int
11644cmdloop(int top)
11645{
11646 union node *n;
11647 struct stackmark smark;
11648 int inter;
11649 int numeof = 0;
11650
11651 TRACE(("cmdloop(%d) called\n", top));
11652 for (;;) {
11653 int skip;
11654
11655 setstackmark(&smark);
11656#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011657 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011658 showjobs(stderr, SHOW_CHANGED);
11659#endif
11660 inter = 0;
11661 if (iflag && top) {
11662 inter++;
11663#if ENABLE_ASH_MAIL
11664 chkmail();
11665#endif
11666 }
11667 n = parsecmd(inter);
11668 /* showtree(n); DEBUG */
11669 if (n == NEOF) {
11670 if (!top || numeof >= 50)
11671 break;
11672 if (!stoppedjobs()) {
11673 if (!Iflag)
11674 break;
11675 out2str("\nUse \"exit\" to leave shell.\n");
11676 }
11677 numeof++;
11678 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011679 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11680 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011681 numeof = 0;
11682 evaltree(n, 0);
11683 }
11684 popstackmark(&smark);
11685 skip = evalskip;
11686
11687 if (skip) {
11688 evalskip = 0;
11689 return skip & SKIPEVAL;
11690 }
11691 }
11692 return 0;
11693}
11694
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011695/*
11696 * Take commands from a file. To be compatible we should do a path
11697 * search for the file, which is necessary to find sub-commands.
11698 */
11699static char *
11700find_dot_file(char *name)
11701{
11702 char *fullname;
11703 const char *path = pathval();
11704 struct stat statb;
11705
11706 /* don't try this for absolute or relative paths */
11707 if (strchr(name, '/'))
11708 return name;
11709
11710 while ((fullname = padvance(&path, name)) != NULL) {
11711 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11712 /*
11713 * Don't bother freeing here, since it will
11714 * be freed by the caller.
11715 */
11716 return fullname;
11717 }
11718 stunalloc(fullname);
11719 }
11720
11721 /* not found in the PATH */
11722 ash_msg_and_raise_error("%s: not found", name);
11723 /* NOTREACHED */
11724}
11725
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011726static int
11727dotcmd(int argc, char **argv)
11728{
11729 struct strlist *sp;
11730 volatile struct shparam saveparam;
11731 int status = 0;
11732
11733 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011734 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011735
Denis Vlasenko68404f12008-03-17 09:00:54 +000011736 if (argv[1]) { /* That's what SVR2 does */
11737 char *fullname = find_dot_file(argv[1]);
11738 argv += 2;
11739 argc -= 2;
11740 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011741 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011742 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011743 shellparam.nparam = argc;
11744 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011745 };
11746
11747 setinputfile(fullname, INPUT_PUSH_FILE);
11748 commandname = fullname;
11749 cmdloop(0);
11750 popfile();
11751
Denis Vlasenko68404f12008-03-17 09:00:54 +000011752 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011753 freeparam(&shellparam);
11754 shellparam = saveparam;
11755 };
11756 status = exitstatus;
11757 }
11758 return status;
11759}
11760
11761static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011762exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011763{
11764 if (stoppedjobs())
11765 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011766 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011767 exitstatus = number(argv[1]);
11768 raise_exception(EXEXIT);
11769 /* NOTREACHED */
11770}
11771
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011772/*
11773 * Read a file containing shell functions.
11774 */
11775static void
11776readcmdfile(char *name)
11777{
11778 setinputfile(name, INPUT_PUSH_FILE);
11779 cmdloop(0);
11780 popfile();
11781}
11782
11783
Denis Vlasenkocc571512007-02-23 21:10:35 +000011784/* ============ find_command inplementation */
11785
11786/*
11787 * Resolve a command name. If you change this routine, you may have to
11788 * change the shellexec routine as well.
11789 */
11790static void
11791find_command(char *name, struct cmdentry *entry, int act, const char *path)
11792{
11793 struct tblentry *cmdp;
11794 int idx;
11795 int prev;
11796 char *fullname;
11797 struct stat statb;
11798 int e;
11799 int updatetbl;
11800 struct builtincmd *bcmd;
11801
11802 /* If name contains a slash, don't use PATH or hash table */
11803 if (strchr(name, '/') != NULL) {
11804 entry->u.index = -1;
11805 if (act & DO_ABS) {
11806 while (stat(name, &statb) < 0) {
11807#ifdef SYSV
11808 if (errno == EINTR)
11809 continue;
11810#endif
11811 entry->cmdtype = CMDUNKNOWN;
11812 return;
11813 }
11814 }
11815 entry->cmdtype = CMDNORMAL;
11816 return;
11817 }
11818
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011819/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011820
11821 updatetbl = (path == pathval());
11822 if (!updatetbl) {
11823 act |= DO_ALTPATH;
11824 if (strstr(path, "%builtin") != NULL)
11825 act |= DO_ALTBLTIN;
11826 }
11827
11828 /* If name is in the table, check answer will be ok */
11829 cmdp = cmdlookup(name, 0);
11830 if (cmdp != NULL) {
11831 int bit;
11832
11833 switch (cmdp->cmdtype) {
11834 default:
11835#if DEBUG
11836 abort();
11837#endif
11838 case CMDNORMAL:
11839 bit = DO_ALTPATH;
11840 break;
11841 case CMDFUNCTION:
11842 bit = DO_NOFUNC;
11843 break;
11844 case CMDBUILTIN:
11845 bit = DO_ALTBLTIN;
11846 break;
11847 }
11848 if (act & bit) {
11849 updatetbl = 0;
11850 cmdp = NULL;
11851 } else if (cmdp->rehash == 0)
11852 /* if not invalidated by cd, we're done */
11853 goto success;
11854 }
11855
11856 /* If %builtin not in path, check for builtin next */
11857 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011858 if (bcmd) {
11859 if (IS_BUILTIN_REGULAR(bcmd))
11860 goto builtin_success;
11861 if (act & DO_ALTPATH) {
11862 if (!(act & DO_ALTBLTIN))
11863 goto builtin_success;
11864 } else if (builtinloc <= 0) {
11865 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011866 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011867 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011868
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011869#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011870 {
11871 int applet_no = find_applet_by_name(name);
11872 if (applet_no >= 0) {
11873 entry->cmdtype = CMDNORMAL;
11874 entry->u.index = -2 - applet_no;
11875 return;
11876 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011877 }
11878#endif
11879
Denis Vlasenkocc571512007-02-23 21:10:35 +000011880 /* We have to search path. */
11881 prev = -1; /* where to start */
11882 if (cmdp && cmdp->rehash) { /* doing a rehash */
11883 if (cmdp->cmdtype == CMDBUILTIN)
11884 prev = builtinloc;
11885 else
11886 prev = cmdp->param.index;
11887 }
11888
11889 e = ENOENT;
11890 idx = -1;
11891 loop:
11892 while ((fullname = padvance(&path, name)) != NULL) {
11893 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011894 /* NB: code below will still use fullname
11895 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011896 idx++;
11897 if (pathopt) {
11898 if (prefix(pathopt, "builtin")) {
11899 if (bcmd)
11900 goto builtin_success;
11901 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011902 }
11903 if ((act & DO_NOFUNC)
11904 || !prefix(pathopt, "func")
11905 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011906 continue;
11907 }
11908 }
11909 /* if rehash, don't redo absolute path names */
11910 if (fullname[0] == '/' && idx <= prev) {
11911 if (idx < prev)
11912 continue;
11913 TRACE(("searchexec \"%s\": no change\n", name));
11914 goto success;
11915 }
11916 while (stat(fullname, &statb) < 0) {
11917#ifdef SYSV
11918 if (errno == EINTR)
11919 continue;
11920#endif
11921 if (errno != ENOENT && errno != ENOTDIR)
11922 e = errno;
11923 goto loop;
11924 }
11925 e = EACCES; /* if we fail, this will be the error */
11926 if (!S_ISREG(statb.st_mode))
11927 continue;
11928 if (pathopt) { /* this is a %func directory */
11929 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011930 /* NB: stalloc will return space pointed by fullname
11931 * (because we don't have any intervening allocations
11932 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011933 readcmdfile(fullname);
11934 cmdp = cmdlookup(name, 0);
11935 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11936 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11937 stunalloc(fullname);
11938 goto success;
11939 }
11940 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11941 if (!updatetbl) {
11942 entry->cmdtype = CMDNORMAL;
11943 entry->u.index = idx;
11944 return;
11945 }
11946 INT_OFF;
11947 cmdp = cmdlookup(name, 1);
11948 cmdp->cmdtype = CMDNORMAL;
11949 cmdp->param.index = idx;
11950 INT_ON;
11951 goto success;
11952 }
11953
11954 /* We failed. If there was an entry for this command, delete it */
11955 if (cmdp && updatetbl)
11956 delete_cmd_entry();
11957 if (act & DO_ERR)
11958 ash_msg("%s: %s", name, errmsg(e, "not found"));
11959 entry->cmdtype = CMDUNKNOWN;
11960 return;
11961
11962 builtin_success:
11963 if (!updatetbl) {
11964 entry->cmdtype = CMDBUILTIN;
11965 entry->u.cmd = bcmd;
11966 return;
11967 }
11968 INT_OFF;
11969 cmdp = cmdlookup(name, 1);
11970 cmdp->cmdtype = CMDBUILTIN;
11971 cmdp->param.cmd = bcmd;
11972 INT_ON;
11973 success:
11974 cmdp->rehash = 0;
11975 entry->cmdtype = cmdp->cmdtype;
11976 entry->u = cmdp->param;
11977}
11978
11979
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011980/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011981
Eric Andersencb57d552001-06-28 07:25:16 +000011982/*
Eric Andersencb57d552001-06-28 07:25:16 +000011983 * The trap builtin.
11984 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011985static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011986trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011987{
11988 char *action;
11989 char **ap;
11990 int signo;
11991
Eric Andersenc470f442003-07-28 09:56:35 +000011992 nextopt(nullstr);
11993 ap = argptr;
11994 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011995 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011996 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011997 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000011998 single_quote(trap[signo]),
11999 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012000 }
12001 }
12002 return 0;
12003 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012004 action = NULL;
12005 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012006 action = *ap++;
12007 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012008 signo = get_signum(*ap);
12009 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012010 ash_msg_and_raise_error("%s: bad trap", *ap);
12011 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012012 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012013 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012014 action = NULL;
12015 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012016 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012017 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012018 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012019 trap[signo] = action;
12020 if (signo != 0)
12021 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012022 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012023 ap++;
12024 }
12025 return 0;
12026}
12027
Eric Andersenc470f442003-07-28 09:56:35 +000012028
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012029/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012030
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012031#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012032/*
12033 * Lists available builtins
12034 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012035static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012036helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012037{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012038 unsigned col;
12039 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012040
12041 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012042 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012043 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012044 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012045 if (col > 60) {
12046 out1fmt("\n");
12047 col = 0;
12048 }
12049 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012050#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012051 {
12052 const char *a = applet_names;
12053 while (*a) {
12054 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12055 if (col > 60) {
12056 out1fmt("\n");
12057 col = 0;
12058 }
12059 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012060 }
12061 }
12062#endif
12063 out1fmt("\n\n");
12064 return EXIT_SUCCESS;
12065}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012066#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012067
Eric Andersencb57d552001-06-28 07:25:16 +000012068/*
Eric Andersencb57d552001-06-28 07:25:16 +000012069 * The export and readonly commands.
12070 */
Eric Andersenc470f442003-07-28 09:56:35 +000012071static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012072exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012073{
12074 struct var *vp;
12075 char *name;
12076 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012077 char **aptr;
12078 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012079
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012080 if (nextopt("p") != 'p') {
12081 aptr = argptr;
12082 name = *aptr;
12083 if (name) {
12084 do {
12085 p = strchr(name, '=');
12086 if (p != NULL) {
12087 p++;
12088 } else {
12089 vp = *findvar(hashvar(name), name);
12090 if (vp) {
12091 vp->flags |= flag;
12092 continue;
12093 }
Eric Andersencb57d552001-06-28 07:25:16 +000012094 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012095 setvar(name, p, flag);
12096 } while ((name = *++aptr) != NULL);
12097 return 0;
12098 }
Eric Andersencb57d552001-06-28 07:25:16 +000012099 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012100 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012101 return 0;
12102}
12103
Eric Andersencb57d552001-06-28 07:25:16 +000012104/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012105 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012106 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012107static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012108unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012109{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012110 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012111
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012112 cmdp = cmdlookup(name, 0);
12113 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12114 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012115}
12116
Eric Andersencb57d552001-06-28 07:25:16 +000012117/*
Eric Andersencb57d552001-06-28 07:25:16 +000012118 * The unset builtin command. We unset the function before we unset the
12119 * variable to allow a function to be unset when there is a readonly variable
12120 * with the same name.
12121 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012122static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012123unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012124{
12125 char **ap;
12126 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012127 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012128 int ret = 0;
12129
12130 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012131 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012132 }
Eric Andersencb57d552001-06-28 07:25:16 +000012133
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012134 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012135 if (flag != 'f') {
12136 i = unsetvar(*ap);
12137 ret |= i;
12138 if (!(i & 2))
12139 continue;
12140 }
12141 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012142 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012143 }
Eric Andersenc470f442003-07-28 09:56:35 +000012144 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012145}
12146
12147
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012148/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012149
Eric Andersenc470f442003-07-28 09:56:35 +000012150#include <sys/times.h>
12151
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012152static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012153 ' ', offsetof(struct tms, tms_utime),
12154 '\n', offsetof(struct tms, tms_stime),
12155 ' ', offsetof(struct tms, tms_cutime),
12156 '\n', offsetof(struct tms, tms_cstime),
12157 0
12158};
Eric Andersencb57d552001-06-28 07:25:16 +000012159
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012160static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012161timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012162{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012163 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012164 const unsigned char *p;
12165 struct tms buf;
12166
12167 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012168 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012169
12170 p = timescmd_str;
12171 do {
12172 t = *(clock_t *)(((char *) &buf) + p[1]);
12173 s = t / clk_tck;
12174 out1fmt("%ldm%ld.%.3lds%c",
12175 s/60, s%60,
12176 ((t - s * clk_tck) * 1000) / clk_tck,
12177 p[0]);
12178 } while (*(p += 2));
12179
Eric Andersencb57d552001-06-28 07:25:16 +000012180 return 0;
12181}
12182
Denis Vlasenko131ae172007-02-18 13:00:19 +000012183#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012184static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012185dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012186{
Eric Andersened9ecf72004-06-22 08:29:45 +000012187 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012188 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012189
Denis Vlasenkob012b102007-02-19 22:43:01 +000012190 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012191 result = arith(s, &errcode);
12192 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012193 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012194 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012195 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012196 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012197 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012198 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012199 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012200 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012201 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012202
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012203 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012204}
Eric Andersenc470f442003-07-28 09:56:35 +000012205
Eric Andersenc470f442003-07-28 09:56:35 +000012206/*
Eric Andersen90898442003-08-06 11:20:52 +000012207 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12208 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12209 *
12210 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012211 */
12212static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012213letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012214{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012215 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012216
Denis Vlasenko68404f12008-03-17 09:00:54 +000012217 argv++;
12218 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012219 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012220 do {
12221 i = dash_arith(*argv);
12222 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012223
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012224 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012225}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012226#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012227
Eric Andersenc470f442003-07-28 09:56:35 +000012228
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012229/* ============ miscbltin.c
12230 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012231 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012232 */
12233
12234#undef rflag
12235
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012236#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012237typedef enum __rlimit_resource rlim_t;
12238#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012239
Eric Andersenc470f442003-07-28 09:56:35 +000012240/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012241 * The read builtin. Options:
12242 * -r Do not interpret '\' specially
12243 * -s Turn off echo (tty only)
12244 * -n NCHARS Read NCHARS max
12245 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12246 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12247 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012248 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012249 * TODO: bash also has:
12250 * -a ARRAY Read into array[0],[1],etc
12251 * -d DELIM End on DELIM char, not newline
12252 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012253 */
Eric Andersenc470f442003-07-28 09:56:35 +000012254static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012255readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012256{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012257 static const char *const arg_REPLY[] = { "REPLY", NULL };
12258
Eric Andersenc470f442003-07-28 09:56:35 +000012259 char **ap;
12260 int backslash;
12261 char c;
12262 int rflag;
12263 char *prompt;
12264 const char *ifs;
12265 char *p;
12266 int startword;
12267 int status;
12268 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012269 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012270#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012271 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012272 int silent = 0;
12273 struct termios tty, old_tty;
12274#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012275#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012276 unsigned end_ms = 0;
12277 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012278#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012279
12280 rflag = 0;
12281 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012282 while ((i = nextopt("p:u:r"
12283 USE_ASH_READ_TIMEOUT("t:")
12284 USE_ASH_READ_NCHARS("n:s")
12285 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012286 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012287 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012288 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012289 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012290#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012291 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012292 nchars = bb_strtou(optionarg, NULL, 10);
12293 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012294 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012295 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012296 break;
12297 case 's':
12298 silent = 1;
12299 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012300#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012301#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012302 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012303 timeout = bb_strtou(optionarg, NULL, 10);
12304 if (errno || timeout > UINT_MAX / 2048)
12305 ash_msg_and_raise_error("invalid timeout");
12306 timeout *= 1000;
12307#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012308 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012309 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012310 /* EINVAL means number is ok, but not terminated by NUL */
12311 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012312 char *p2;
12313 if (*++p) {
12314 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012315 ts.tv_usec = bb_strtou(p, &p2, 10);
12316 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012317 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012318 scale = p2 - p;
12319 /* normalize to usec */
12320 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012321 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012322 while (scale++ < 6)
12323 ts.tv_usec *= 10;
12324 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012325 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012326 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012327 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012328 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012329 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012330 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012331#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012332 break;
12333#endif
12334 case 'r':
12335 rflag = 1;
12336 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012337 case 'u':
12338 fd = bb_strtou(optionarg, NULL, 10);
12339 if (fd < 0 || errno)
12340 ash_msg_and_raise_error("invalid file descriptor");
12341 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012342 default:
12343 break;
12344 }
Eric Andersenc470f442003-07-28 09:56:35 +000012345 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012346 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012347 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012348 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012349 ap = argptr;
12350 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012351 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012352 ifs = bltinlookup("IFS");
12353 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012354 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012355#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012356 tcgetattr(fd, &tty);
12357 old_tty = tty;
12358 if (nchars || silent) {
12359 if (nchars) {
12360 tty.c_lflag &= ~ICANON;
12361 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012362 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012363 if (silent) {
12364 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12365 }
12366 /* if tcgetattr failed, tcsetattr will fail too.
12367 * Ignoring, it's harmless. */
12368 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012369 }
12370#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012371
Eric Andersenc470f442003-07-28 09:56:35 +000012372 status = 0;
12373 startword = 1;
12374 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012375#if ENABLE_ASH_READ_TIMEOUT
12376 if (timeout) /* NB: ensuring end_ms is nonzero */
12377 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12378#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012379 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012380 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012381#if ENABLE_ASH_READ_TIMEOUT
12382 if (end_ms) {
12383 struct pollfd pfd[1];
12384 pfd[0].fd = fd;
12385 pfd[0].events = POLLIN;
12386 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12387 if ((int)timeout <= 0 /* already late? */
12388 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12389 ) { /* timed out! */
12390#if ENABLE_ASH_READ_NCHARS
12391 tcsetattr(fd, TCSANOW, &old_tty);
12392#endif
12393 return 1;
12394 }
12395 }
12396#endif
12397 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012398 status = 1;
12399 break;
12400 }
12401 if (c == '\0')
12402 continue;
12403 if (backslash) {
12404 backslash = 0;
12405 if (c != '\n')
12406 goto put;
12407 continue;
12408 }
12409 if (!rflag && c == '\\') {
12410 backslash++;
12411 continue;
12412 }
12413 if (c == '\n')
12414 break;
12415 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12416 continue;
12417 }
12418 startword = 0;
12419 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12420 STACKSTRNUL(p);
12421 setvar(*ap, stackblock(), 0);
12422 ap++;
12423 startword = 1;
12424 STARTSTACKSTR(p);
12425 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012426 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012427 STPUTC(c, p);
12428 }
12429 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012430/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012431#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012432 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012433#else
12434 while (1);
12435#endif
12436
12437#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012438 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012439#endif
12440
Eric Andersenc470f442003-07-28 09:56:35 +000012441 STACKSTRNUL(p);
12442 /* Remove trailing blanks */
12443 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12444 *p = '\0';
12445 setvar(*ap, stackblock(), 0);
12446 while (*++ap != NULL)
12447 setvar(*ap, nullstr, 0);
12448 return status;
12449}
12450
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012451static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012452umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012453{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012454 static const char permuser[3] ALIGN1 = "ugo";
12455 static const char permmode[3] ALIGN1 = "rwx";
12456 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012457 S_IRUSR, S_IWUSR, S_IXUSR,
12458 S_IRGRP, S_IWGRP, S_IXGRP,
12459 S_IROTH, S_IWOTH, S_IXOTH
12460 };
12461
12462 char *ap;
12463 mode_t mask;
12464 int i;
12465 int symbolic_mode = 0;
12466
12467 while (nextopt("S") != '\0') {
12468 symbolic_mode = 1;
12469 }
12470
Denis Vlasenkob012b102007-02-19 22:43:01 +000012471 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012472 mask = umask(0);
12473 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012474 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012475
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012476 ap = *argptr;
12477 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012478 if (symbolic_mode) {
12479 char buf[18];
12480 char *p = buf;
12481
12482 for (i = 0; i < 3; i++) {
12483 int j;
12484
12485 *p++ = permuser[i];
12486 *p++ = '=';
12487 for (j = 0; j < 3; j++) {
12488 if ((mask & permmask[3 * i + j]) == 0) {
12489 *p++ = permmode[j];
12490 }
12491 }
12492 *p++ = ',';
12493 }
12494 *--p = 0;
12495 puts(buf);
12496 } else {
12497 out1fmt("%.4o\n", mask);
12498 }
12499 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012500 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012501 mask = 0;
12502 do {
12503 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012504 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012505 mask = (mask << 3) + (*ap - '0');
12506 } while (*++ap != '\0');
12507 umask(mask);
12508 } else {
12509 mask = ~mask & 0777;
12510 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012511 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012512 }
12513 umask(~mask & 0777);
12514 }
12515 }
12516 return 0;
12517}
12518
12519/*
12520 * ulimit builtin
12521 *
12522 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12523 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12524 * ash by J.T. Conklin.
12525 *
12526 * Public domain.
12527 */
12528
12529struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012530 uint8_t cmd; /* RLIMIT_xxx fit into it */
12531 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012532 char option;
12533};
12534
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012535static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012536#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012537 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012538#endif
12539#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012540 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012541#endif
12542#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012543 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012544#endif
12545#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012546 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012547#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012548#ifdef RLIMIT_CORE
12549 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012550#endif
12551#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012552 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012553#endif
12554#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012555 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012556#endif
12557#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012558 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012559#endif
12560#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012561 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012562#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012563#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012564 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012565#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012566#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012567 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012568#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012569};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012570static const char limits_name[] =
12571#ifdef RLIMIT_CPU
12572 "time(seconds)" "\0"
12573#endif
12574#ifdef RLIMIT_FSIZE
12575 "file(blocks)" "\0"
12576#endif
12577#ifdef RLIMIT_DATA
12578 "data(kb)" "\0"
12579#endif
12580#ifdef RLIMIT_STACK
12581 "stack(kb)" "\0"
12582#endif
12583#ifdef RLIMIT_CORE
12584 "coredump(blocks)" "\0"
12585#endif
12586#ifdef RLIMIT_RSS
12587 "memory(kb)" "\0"
12588#endif
12589#ifdef RLIMIT_MEMLOCK
12590 "locked memory(kb)" "\0"
12591#endif
12592#ifdef RLIMIT_NPROC
12593 "process" "\0"
12594#endif
12595#ifdef RLIMIT_NOFILE
12596 "nofiles" "\0"
12597#endif
12598#ifdef RLIMIT_AS
12599 "vmemory(kb)" "\0"
12600#endif
12601#ifdef RLIMIT_LOCKS
12602 "locks" "\0"
12603#endif
12604;
Eric Andersenc470f442003-07-28 09:56:35 +000012605
Glenn L McGrath76620622004-01-13 10:19:37 +000012606enum limtype { SOFT = 0x1, HARD = 0x2 };
12607
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012608static void
12609printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012610 const struct limits *l)
12611{
12612 rlim_t val;
12613
12614 val = limit->rlim_max;
12615 if (how & SOFT)
12616 val = limit->rlim_cur;
12617
12618 if (val == RLIM_INFINITY)
12619 out1fmt("unlimited\n");
12620 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012621 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012622 out1fmt("%lld\n", (long long) val);
12623 }
12624}
12625
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012626static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012627ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012628{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012629 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012630 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012631 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012632 const struct limits *l;
12633 int set, all = 0;
12634 int optc, what;
12635 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012636
12637 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012638 while ((optc = nextopt("HSa"
12639#ifdef RLIMIT_CPU
12640 "t"
12641#endif
12642#ifdef RLIMIT_FSIZE
12643 "f"
12644#endif
12645#ifdef RLIMIT_DATA
12646 "d"
12647#endif
12648#ifdef RLIMIT_STACK
12649 "s"
12650#endif
12651#ifdef RLIMIT_CORE
12652 "c"
12653#endif
12654#ifdef RLIMIT_RSS
12655 "m"
12656#endif
12657#ifdef RLIMIT_MEMLOCK
12658 "l"
12659#endif
12660#ifdef RLIMIT_NPROC
12661 "p"
12662#endif
12663#ifdef RLIMIT_NOFILE
12664 "n"
12665#endif
12666#ifdef RLIMIT_AS
12667 "v"
12668#endif
12669#ifdef RLIMIT_LOCKS
12670 "w"
12671#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012672 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012673 switch (optc) {
12674 case 'H':
12675 how = HARD;
12676 break;
12677 case 'S':
12678 how = SOFT;
12679 break;
12680 case 'a':
12681 all = 1;
12682 break;
12683 default:
12684 what = optc;
12685 }
12686
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012687 for (l = limits_tbl; l->option != what; l++)
12688 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012689
12690 set = *argptr ? 1 : 0;
12691 if (set) {
12692 char *p = *argptr;
12693
12694 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012695 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012696 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012697 val = RLIM_INFINITY;
12698 else {
12699 val = (rlim_t) 0;
12700
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012701 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012702 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012703 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012704 if (val < (rlim_t) 0)
12705 break;
12706 }
12707 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012708 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012709 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012710 }
12711 }
12712 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012713 const char *lname = limits_name;
12714 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012715 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012716 out1fmt("%-20s ", lname);
12717 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012718 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012719 }
12720 return 0;
12721 }
12722
12723 getrlimit(l->cmd, &limit);
12724 if (set) {
12725 if (how & HARD)
12726 limit.rlim_max = val;
12727 if (how & SOFT)
12728 limit.rlim_cur = val;
12729 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012730 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012731 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012732 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012733 }
12734 return 0;
12735}
12736
Eric Andersen90898442003-08-06 11:20:52 +000012737
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012738/* ============ Math support */
12739
Denis Vlasenko131ae172007-02-18 13:00:19 +000012740#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012741
12742/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12743
12744 Permission is hereby granted, free of charge, to any person obtaining
12745 a copy of this software and associated documentation files (the
12746 "Software"), to deal in the Software without restriction, including
12747 without limitation the rights to use, copy, modify, merge, publish,
12748 distribute, sublicense, and/or sell copies of the Software, and to
12749 permit persons to whom the Software is furnished to do so, subject to
12750 the following conditions:
12751
12752 The above copyright notice and this permission notice shall be
12753 included in all copies or substantial portions of the Software.
12754
12755 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12756 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12757 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12758 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12759 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12760 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12761 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12762*/
12763
12764/* This is my infix parser/evaluator. It is optimized for size, intended
12765 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012766 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012767 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012768 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012769 * be that which POSIX specifies for shells. */
12770
12771/* The code uses a simple two-stack algorithm. See
12772 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012773 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012774 * this is based (this code differs in that it applies operators immediately
12775 * to the stack instead of adding them to a queue to end up with an
12776 * expression). */
12777
12778/* To use the routine, call it with an expression string and error return
12779 * pointer */
12780
12781/*
12782 * Aug 24, 2001 Manuel Novoa III
12783 *
12784 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12785 *
12786 * 1) In arith_apply():
12787 * a) Cached values of *numptr and &(numptr[-1]).
12788 * b) Removed redundant test for zero denominator.
12789 *
12790 * 2) In arith():
12791 * a) Eliminated redundant code for processing operator tokens by moving
12792 * to a table-based implementation. Also folded handling of parens
12793 * into the table.
12794 * b) Combined all 3 loops which called arith_apply to reduce generated
12795 * code size at the cost of speed.
12796 *
12797 * 3) The following expressions were treated as valid by the original code:
12798 * 1() , 0! , 1 ( *3 ) .
12799 * These bugs have been fixed by internally enclosing the expression in
12800 * parens and then checking that all binary ops and right parens are
12801 * preceded by a valid expression (NUM_TOKEN).
12802 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012803 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012804 * ctype's isspace() if it is used by another busybox applet or if additional
12805 * whitespace chars should be considered. Look below the "#include"s for a
12806 * precompiler test.
12807 */
12808
12809/*
12810 * Aug 26, 2001 Manuel Novoa III
12811 *
12812 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12813 *
12814 * Merge in Aaron's comments previously posted to the busybox list,
12815 * modified slightly to take account of my changes to the code.
12816 *
12817 */
12818
12819/*
12820 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12821 *
12822 * - allow access to variable,
12823 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12824 * - realize assign syntax (VAR=expr, +=, *= etc)
12825 * - realize exponentiation (** operator)
12826 * - realize comma separated - expr, expr
12827 * - realise ++expr --expr expr++ expr--
12828 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012829 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012830 * - was restored loses XOR operator
12831 * - remove one goto label, added three ;-)
12832 * - protect $((num num)) as true zero expr (Manuel`s error)
12833 * - always use special isspace(), see comment from bash ;-)
12834 */
12835
Eric Andersen90898442003-08-06 11:20:52 +000012836#define arith_isspace(arithval) \
12837 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12838
Eric Andersen90898442003-08-06 11:20:52 +000012839typedef unsigned char operator;
12840
12841/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012842 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012843 * precedence. The ID portion is so that multiple operators can have the
12844 * same precedence, ensuring that the leftmost one is evaluated first.
12845 * Consider * and /. */
12846
12847#define tok_decl(prec,id) (((id)<<5)|(prec))
12848#define PREC(op) ((op) & 0x1F)
12849
12850#define TOK_LPAREN tok_decl(0,0)
12851
12852#define TOK_COMMA tok_decl(1,0)
12853
12854#define TOK_ASSIGN tok_decl(2,0)
12855#define TOK_AND_ASSIGN tok_decl(2,1)
12856#define TOK_OR_ASSIGN tok_decl(2,2)
12857#define TOK_XOR_ASSIGN tok_decl(2,3)
12858#define TOK_PLUS_ASSIGN tok_decl(2,4)
12859#define TOK_MINUS_ASSIGN tok_decl(2,5)
12860#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12861#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12862
12863#define TOK_MUL_ASSIGN tok_decl(3,0)
12864#define TOK_DIV_ASSIGN tok_decl(3,1)
12865#define TOK_REM_ASSIGN tok_decl(3,2)
12866
12867/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012868#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012869
12870/* conditional is right associativity too */
12871#define TOK_CONDITIONAL tok_decl(4,0)
12872#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12873
12874#define TOK_OR tok_decl(5,0)
12875
12876#define TOK_AND tok_decl(6,0)
12877
12878#define TOK_BOR tok_decl(7,0)
12879
12880#define TOK_BXOR tok_decl(8,0)
12881
12882#define TOK_BAND tok_decl(9,0)
12883
12884#define TOK_EQ tok_decl(10,0)
12885#define TOK_NE tok_decl(10,1)
12886
12887#define TOK_LT tok_decl(11,0)
12888#define TOK_GT tok_decl(11,1)
12889#define TOK_GE tok_decl(11,2)
12890#define TOK_LE tok_decl(11,3)
12891
12892#define TOK_LSHIFT tok_decl(12,0)
12893#define TOK_RSHIFT tok_decl(12,1)
12894
12895#define TOK_ADD tok_decl(13,0)
12896#define TOK_SUB tok_decl(13,1)
12897
12898#define TOK_MUL tok_decl(14,0)
12899#define TOK_DIV tok_decl(14,1)
12900#define TOK_REM tok_decl(14,2)
12901
12902/* exponent is right associativity */
12903#define TOK_EXPONENT tok_decl(15,1)
12904
12905/* For now unary operators. */
12906#define UNARYPREC 16
12907#define TOK_BNOT tok_decl(UNARYPREC,0)
12908#define TOK_NOT tok_decl(UNARYPREC,1)
12909
12910#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12911#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12912
12913#define PREC_PRE (UNARYPREC+2)
12914
12915#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12916#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12917
12918#define PREC_POST (UNARYPREC+3)
12919
12920#define TOK_POST_INC tok_decl(PREC_POST, 0)
12921#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12922
12923#define SPEC_PREC (UNARYPREC+4)
12924
12925#define TOK_NUM tok_decl(SPEC_PREC, 0)
12926#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12927
12928#define NUMPTR (*numstackptr)
12929
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012930static int
12931tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012932{
12933 operator prec = PREC(op);
12934
12935 convert_prec_is_assing(prec);
12936 return (prec == PREC(TOK_ASSIGN) ||
12937 prec == PREC_PRE || prec == PREC_POST);
12938}
12939
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012940static int
12941is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012942{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012943 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12944 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012945}
12946
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012947typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012948 arith_t val;
12949 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012950 char contidional_second_val_initialized;
12951 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012952 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012953} v_n_t;
12954
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012955typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012956 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012957 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012958} chk_var_recursive_looped_t;
12959
12960static chk_var_recursive_looped_t *prev_chk_var_recursive;
12961
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012962static int
12963arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012964{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012965 if (t->var) {
12966 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012967
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012968 if (p) {
12969 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012970
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012971 /* recursive try as expression */
12972 chk_var_recursive_looped_t *cur;
12973 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012974
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012975 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12976 if (strcmp(cur->var, t->var) == 0) {
12977 /* expression recursion loop detected */
12978 return -5;
12979 }
12980 }
12981 /* save current lookuped var name */
12982 cur = prev_chk_var_recursive;
12983 cur_save.var = t->var;
12984 cur_save.next = cur;
12985 prev_chk_var_recursive = &cur_save;
12986
12987 t->val = arith (p, &errcode);
12988 /* restore previous ptr after recursiving */
12989 prev_chk_var_recursive = cur;
12990 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012991 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012992 /* allow undefined var as 0 */
12993 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012994 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012995 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012996}
12997
12998/* "applying" a token means performing it on the top elements on the integer
12999 * stack. For a unary operator it will only change the top element, but a
13000 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013001static int
13002arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013003{
Eric Andersen90898442003-08-06 11:20:52 +000013004 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013005 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013006 int ret_arith_lookup_val;
13007
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013008 /* There is no operator that can work without arguments */
13009 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013010 numptr_m1 = NUMPTR - 1;
13011
13012 /* check operand is var with noninteger value */
13013 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013014 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013015 return ret_arith_lookup_val;
13016
13017 rez = numptr_m1->val;
13018 if (op == TOK_UMINUS)
13019 rez *= -1;
13020 else if (op == TOK_NOT)
13021 rez = !rez;
13022 else if (op == TOK_BNOT)
13023 rez = ~rez;
13024 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13025 rez++;
13026 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13027 rez--;
13028 else if (op != TOK_UPLUS) {
13029 /* Binary operators */
13030
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013031 /* check and binary operators need two arguments */
13032 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013033
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013034 /* ... and they pop one */
13035 --NUMPTR;
13036 numptr_val = rez;
13037 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013038 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013039 /* protect $((expr1 ? expr2)) without ": expr" */
13040 goto err;
13041 }
13042 rez = numptr_m1->contidional_second_val;
13043 } else if (numptr_m1->contidional_second_val_initialized) {
13044 /* protect $((expr1 : expr2)) without "expr ? " */
13045 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013046 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013047 numptr_m1 = NUMPTR - 1;
13048 if (op != TOK_ASSIGN) {
13049 /* check operand is var with noninteger value for not '=' */
13050 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13051 if (ret_arith_lookup_val)
13052 return ret_arith_lookup_val;
13053 }
13054 if (op == TOK_CONDITIONAL) {
13055 numptr_m1->contidional_second_val = rez;
13056 }
13057 rez = numptr_m1->val;
13058 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013059 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013060 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013061 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013062 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013063 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013064 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013065 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013066 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013067 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013068 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013069 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013070 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013071 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013072 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013073 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013074 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013075 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013076 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013077 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013078 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013079 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013080 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013081 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013082 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013083 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013084 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013085 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013087 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013088 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013089 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013090 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013091 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013092 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013093 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013094 /* protect $((expr : expr)) without "expr ? " */
13095 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013096 }
13097 numptr_m1->contidional_second_val_initialized = op;
13098 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013099 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013100 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013101 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013102 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013103 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013104 return -3; /* exponent less than 0 */
13105 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013106 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013107
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013108 if (numptr_val)
13109 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013110 c *= rez;
13111 rez = c;
13112 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013113 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013114 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013115 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013116 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013117 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013118 rez %= numptr_val;
13119 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013120 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013121 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013122
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013123 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013124 /* Hmm, 1=2 ? */
13125 goto err;
13126 }
13127 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013128#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013129 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013130#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013131 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013132#endif
Eric Andersen90898442003-08-06 11:20:52 +000013133 setvar(numptr_m1->var, buf, 0);
13134 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013135 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013136 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013137 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013138 rez++;
13139 }
13140 numptr_m1->val = rez;
13141 /* protect geting var value, is number now */
13142 numptr_m1->var = NULL;
13143 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013144 err:
13145 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013146}
13147
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013148/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013149static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013150 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13151 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13152 '<','<', 0, TOK_LSHIFT,
13153 '>','>', 0, TOK_RSHIFT,
13154 '|','|', 0, TOK_OR,
13155 '&','&', 0, TOK_AND,
13156 '!','=', 0, TOK_NE,
13157 '<','=', 0, TOK_LE,
13158 '>','=', 0, TOK_GE,
13159 '=','=', 0, TOK_EQ,
13160 '|','=', 0, TOK_OR_ASSIGN,
13161 '&','=', 0, TOK_AND_ASSIGN,
13162 '*','=', 0, TOK_MUL_ASSIGN,
13163 '/','=', 0, TOK_DIV_ASSIGN,
13164 '%','=', 0, TOK_REM_ASSIGN,
13165 '+','=', 0, TOK_PLUS_ASSIGN,
13166 '-','=', 0, TOK_MINUS_ASSIGN,
13167 '-','-', 0, TOK_POST_DEC,
13168 '^','=', 0, TOK_XOR_ASSIGN,
13169 '+','+', 0, TOK_POST_INC,
13170 '*','*', 0, TOK_EXPONENT,
13171 '!', 0, TOK_NOT,
13172 '<', 0, TOK_LT,
13173 '>', 0, TOK_GT,
13174 '=', 0, TOK_ASSIGN,
13175 '|', 0, TOK_BOR,
13176 '&', 0, TOK_BAND,
13177 '*', 0, TOK_MUL,
13178 '/', 0, TOK_DIV,
13179 '%', 0, TOK_REM,
13180 '+', 0, TOK_ADD,
13181 '-', 0, TOK_SUB,
13182 '^', 0, TOK_BXOR,
13183 /* uniq */
13184 '~', 0, TOK_BNOT,
13185 ',', 0, TOK_COMMA,
13186 '?', 0, TOK_CONDITIONAL,
13187 ':', 0, TOK_CONDITIONAL_SEP,
13188 ')', 0, TOK_RPAREN,
13189 '(', 0, TOK_LPAREN,
13190 0
13191};
13192/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013193#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013194
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013195static arith_t
13196arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013197{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013198 char arithval; /* Current character under analysis */
13199 operator lasttok, op;
13200 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013201 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013202 const char *p = endexpression;
13203 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013204 v_n_t *numstack, *numstackptr;
13205 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013206
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013207 /* Stack of integers */
13208 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13209 * in any given correct or incorrect expression is left as an exercise to
13210 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013211 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013212 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013213 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013214
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013215 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13216 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013217
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013218 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013219 arithval = *expr;
13220 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013221 if (p == endexpression) {
13222 /* Null expression. */
13223 return 0;
13224 }
13225
13226 /* This is only reached after all tokens have been extracted from the
13227 * input stream. If there are still tokens on the operator stack, they
13228 * are to be applied in order. At the end, there should be a final
13229 * result on the integer stack */
13230
13231 if (expr != endexpression + 1) {
13232 /* If we haven't done so already, */
13233 /* append a closing right paren */
13234 expr = endexpression;
13235 /* and let the loop process it. */
13236 continue;
13237 }
13238 /* At this point, we're done with the expression. */
13239 if (numstackptr != numstack+1) {
13240 /* ... but if there isn't, it's bad */
13241 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013242 *perrcode = -1;
13243 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013244 }
13245 if (numstack->var) {
13246 /* expression is $((var)) only, lookup now */
13247 errcode = arith_lookup_val(numstack);
13248 }
13249 ret:
13250 *perrcode = errcode;
13251 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013252 }
13253
Eric Andersen90898442003-08-06 11:20:52 +000013254 /* Continue processing the expression. */
13255 if (arith_isspace(arithval)) {
13256 /* Skip whitespace */
13257 goto prologue;
13258 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013259 p = endofname(expr);
13260 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013261 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013262
13263 numstackptr->var = alloca(var_name_size);
13264 safe_strncpy(numstackptr->var, expr, var_name_size);
13265 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013266 num:
Eric Andersen90898442003-08-06 11:20:52 +000013267 numstackptr->contidional_second_val_initialized = 0;
13268 numstackptr++;
13269 lasttok = TOK_NUM;
13270 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013271 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013272 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013273 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013274#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013275 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013276#else
13277 numstackptr->val = strtol(expr, (char **) &expr, 0);
13278#endif
Eric Andersen90898442003-08-06 11:20:52 +000013279 goto num;
13280 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013281 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013282 const char *o;
13283
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013284 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013285 /* strange operator not found */
13286 goto err;
13287 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013288 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013289 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013290 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013291 /* found */
13292 expr = o - 1;
13293 break;
13294 }
13295 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013296 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013297 p++;
13298 /* skip zero delim */
13299 p++;
13300 }
13301 op = p[1];
13302
13303 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013304 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13305 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013306
13307 /* Plus and minus are binary (not unary) _only_ if the last
13308 * token was as number, or a right paren (which pretends to be
13309 * a number, since it evaluates to one). Think about it.
13310 * It makes sense. */
13311 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013312 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013313 case TOK_ADD:
13314 op = TOK_UPLUS;
13315 break;
13316 case TOK_SUB:
13317 op = TOK_UMINUS;
13318 break;
13319 case TOK_POST_INC:
13320 op = TOK_PRE_INC;
13321 break;
13322 case TOK_POST_DEC:
13323 op = TOK_PRE_DEC;
13324 break;
Eric Andersen90898442003-08-06 11:20:52 +000013325 }
13326 }
13327 /* We don't want a unary operator to cause recursive descent on the
13328 * stack, because there can be many in a row and it could cause an
13329 * operator to be evaluated before its argument is pushed onto the
13330 * integer stack. */
13331 /* But for binary operators, "apply" everything on the operator
13332 * stack until we find an operator with a lesser priority than the
13333 * one we have just extracted. */
13334 /* Left paren is given the lowest priority so it will never be
13335 * "applied" in this way.
13336 * if associativity is right and priority eq, applied also skip
13337 */
13338 prec = PREC(op);
13339 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13340 /* not left paren or unary */
13341 if (lasttok != TOK_NUM) {
13342 /* binary op must be preceded by a num */
13343 goto err;
13344 }
13345 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013346 if (op == TOK_RPAREN) {
13347 /* The algorithm employed here is simple: while we don't
13348 * hit an open paren nor the bottom of the stack, pop
13349 * tokens and apply them */
13350 if (stackptr[-1] == TOK_LPAREN) {
13351 --stackptr;
13352 /* Any operator directly after a */
13353 lasttok = TOK_NUM;
13354 /* close paren should consider itself binary */
13355 goto prologue;
13356 }
13357 } else {
13358 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013359
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013360 convert_prec_is_assing(prec);
13361 convert_prec_is_assing(prev_prec);
13362 if (prev_prec < prec)
13363 break;
13364 /* check right assoc */
13365 if (prev_prec == prec && is_right_associativity(prec))
13366 break;
13367 }
13368 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13369 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013370 }
13371 if (op == TOK_RPAREN) {
13372 goto err;
13373 }
13374 }
13375
13376 /* Push this operator to the stack and remember it. */
13377 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013378 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013379 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013380 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013381}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013382#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013383
13384
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013385/* ============ main() and helpers */
13386
13387/*
13388 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013389 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013390static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013391static void
13392exitshell(void)
13393{
13394 struct jmploc loc;
13395 char *p;
13396 int status;
13397
13398 status = exitstatus;
13399 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13400 if (setjmp(loc.loc)) {
13401 if (exception == EXEXIT)
13402/* dash bug: it just does _exit(exitstatus) here
13403 * but we have to do setjobctl(0) first!
13404 * (bug is still not fixed in dash-0.5.3 - if you run dash
13405 * under Midnight Commander, on exit from dash MC is backgrounded) */
13406 status = exitstatus;
13407 goto out;
13408 }
13409 exception_handler = &loc;
13410 p = trap[0];
13411 if (p) {
13412 trap[0] = NULL;
13413 evalstring(p, 0);
13414 }
13415 flush_stdout_stderr();
13416 out:
13417 setjobctl(0);
13418 _exit(status);
13419 /* NOTREACHED */
13420}
13421
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013422static void
13423init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013424{
13425 /* from input.c: */
13426 basepf.nextc = basepf.buf = basebuf;
13427
13428 /* from trap.c: */
13429 signal(SIGCHLD, SIG_DFL);
13430
13431 /* from var.c: */
13432 {
13433 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013434 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013435 const char *p;
13436 struct stat st1, st2;
13437
13438 initvar();
13439 for (envp = environ; envp && *envp; envp++) {
13440 if (strchr(*envp, '=')) {
13441 setvareq(*envp, VEXPORT|VTEXTFIXED);
13442 }
13443 }
13444
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013445 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013446 setvar("PPID", ppid, 0);
13447
13448 p = lookupvar("PWD");
13449 if (p)
13450 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13451 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13452 p = '\0';
13453 setpwd(p, 0);
13454 }
13455}
13456
13457/*
13458 * Process the shell command line arguments.
13459 */
13460static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013461procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013462{
13463 int i;
13464 const char *xminusc;
13465 char **xargv;
13466
13467 xargv = argv;
13468 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013469 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013470 xargv++;
13471 for (i = 0; i < NOPTS; i++)
13472 optlist[i] = 2;
13473 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013474 if (options(1)) {
13475 /* it already printed err message */
13476 raise_exception(EXERROR);
13477 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013478 xargv = argptr;
13479 xminusc = minusc;
13480 if (*xargv == NULL) {
13481 if (xminusc)
13482 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13483 sflag = 1;
13484 }
13485 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13486 iflag = 1;
13487 if (mflag == 2)
13488 mflag = iflag;
13489 for (i = 0; i < NOPTS; i++)
13490 if (optlist[i] == 2)
13491 optlist[i] = 0;
13492#if DEBUG == 2
13493 debug = 1;
13494#endif
13495 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13496 if (xminusc) {
13497 minusc = *xargv++;
13498 if (*xargv)
13499 goto setarg0;
13500 } else if (!sflag) {
13501 setinputfile(*xargv, 0);
13502 setarg0:
13503 arg0 = *xargv++;
13504 commandname = arg0;
13505 }
13506
13507 shellparam.p = xargv;
13508#if ENABLE_ASH_GETOPTS
13509 shellparam.optind = 1;
13510 shellparam.optoff = -1;
13511#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013512 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013513 while (*xargv) {
13514 shellparam.nparam++;
13515 xargv++;
13516 }
13517 optschanged();
13518}
13519
13520/*
13521 * Read /etc/profile or .profile.
13522 */
13523static void
13524read_profile(const char *name)
13525{
13526 int skip;
13527
13528 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13529 return;
13530 skip = cmdloop(0);
13531 popfile();
13532 if (skip)
13533 exitshell();
13534}
13535
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013536/*
13537 * This routine is called when an error or an interrupt occurs in an
13538 * interactive shell and control is returned to the main command loop.
13539 */
13540static void
13541reset(void)
13542{
13543 /* from eval.c: */
13544 evalskip = 0;
13545 loopnest = 0;
13546 /* from input.c: */
13547 parselleft = parsenleft = 0; /* clear input buffer */
13548 popallfiles();
13549 /* from parser.c: */
13550 tokpushback = 0;
13551 checkkwd = 0;
13552 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013553 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013554}
13555
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013556#if PROFILE
13557static short profile_buf[16384];
13558extern int etext();
13559#endif
13560
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013561/*
13562 * Main routine. We initialize things, parse the arguments, execute
13563 * profiles if we're a login shell, and then call cmdloop to execute
13564 * commands. The setjmp call sets up the location to jump to when an
13565 * exception occurs. When an exception occurs the variable "state"
13566 * is used to figure out how far we had gotten.
13567 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013568int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013569int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013570{
13571 char *shinit;
13572 volatile int state;
13573 struct jmploc jmploc;
13574 struct stackmark smark;
13575
Denis Vlasenko01631112007-12-16 17:20:38 +000013576 /* Initialize global data */
13577 INIT_G_misc();
13578 INIT_G_memstack();
13579 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013580#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013581 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013582#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013583 INIT_G_cmdtable();
13584
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013585#if PROFILE
13586 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13587#endif
13588
13589#if ENABLE_FEATURE_EDITING
13590 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13591#endif
13592 state = 0;
13593 if (setjmp(jmploc.loc)) {
13594 int e;
13595 int s;
13596
13597 reset();
13598
13599 e = exception;
13600 if (e == EXERROR)
13601 exitstatus = 2;
13602 s = state;
13603 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13604 exitshell();
13605
13606 if (e == EXINT) {
13607 outcslow('\n', stderr);
13608 }
13609 popstackmark(&smark);
13610 FORCE_INT_ON; /* enable interrupts */
13611 if (s == 1)
13612 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013613 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013614 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013615 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013616 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013617 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013618 }
13619 exception_handler = &jmploc;
13620#if DEBUG
13621 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013622 trace_puts("Shell args: ");
13623 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013624#endif
13625 rootpid = getpid();
13626
13627#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013628 /* Can use monotonic_ns() for better randomness but for now it is
13629 * not used anywhere else in busybox... so avoid bloat */
13630 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013631#endif
13632 init();
13633 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013634 procargs(argv);
13635
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013636#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13637 if (iflag) {
13638 const char *hp = lookupvar("HISTFILE");
13639
13640 if (hp == NULL) {
13641 hp = lookupvar("HOME");
13642 if (hp != NULL) {
13643 char *defhp = concat_path_file(hp, ".ash_history");
13644 setvar("HISTFILE", defhp, 0);
13645 free(defhp);
13646 }
13647 }
13648 }
13649#endif
13650 if (argv[0] && argv[0][0] == '-')
13651 isloginsh = 1;
13652 if (isloginsh) {
13653 state = 1;
13654 read_profile("/etc/profile");
13655 state1:
13656 state = 2;
13657 read_profile(".profile");
13658 }
13659 state2:
13660 state = 3;
13661 if (
13662#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013663 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013664#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013665 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013666 ) {
13667 shinit = lookupvar("ENV");
13668 if (shinit != NULL && *shinit != '\0') {
13669 read_profile(shinit);
13670 }
13671 }
13672 state3:
13673 state = 4;
13674 if (minusc)
13675 evalstring(minusc, 0);
13676
13677 if (sflag || minusc == NULL) {
13678#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013679 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013680 const char *hp = lookupvar("HISTFILE");
13681
13682 if (hp != NULL)
13683 line_input_state->hist_file = hp;
13684 }
13685#endif
13686 state4: /* XXX ??? - why isn't this before the "if" statement */
13687 cmdloop(1);
13688 }
13689#if PROFILE
13690 monitor(0);
13691#endif
13692#ifdef GPROF
13693 {
13694 extern void _mcleanup(void);
13695 _mcleanup();
13696 }
13697#endif
13698 exitshell();
13699 /* NOTREACHED */
13700}
13701
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013702#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013703const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013704int main(int argc, char **argv)
13705{
13706 return ash_main(argc, argv);
13707}
13708#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013709
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013710
Eric Andersendf82f612001-06-28 07:46:40 +000013711/*-
13712 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013713 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013714 *
13715 * This code is derived from software contributed to Berkeley by
13716 * Kenneth Almquist.
13717 *
13718 * Redistribution and use in source and binary forms, with or without
13719 * modification, are permitted provided that the following conditions
13720 * are met:
13721 * 1. Redistributions of source code must retain the above copyright
13722 * notice, this list of conditions and the following disclaimer.
13723 * 2. Redistributions in binary form must reproduce the above copyright
13724 * notice, this list of conditions and the following disclaimer in the
13725 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013726 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013727 * may be used to endorse or promote products derived from this software
13728 * without specific prior written permission.
13729 *
13730 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13731 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13732 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13733 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13734 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13735 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13736 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13737 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13738 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13739 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13740 * SUCH DAMAGE.
13741 */