blob: 81ac563fb3f5b805b4054da925c03a06888f979e [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 Vlasenko834dee72008-10-07 09:18:30 +00002575
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002576#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002577/* buffer for top level input file */
2578#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002579
Eric Andersenc470f442003-07-28 09:56:35 +00002580/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002581#define CWORD 0 /* character is nothing special */
2582#define CNL 1 /* newline character */
2583#define CBACK 2 /* a backslash character */
2584#define CSQUOTE 3 /* single quote */
2585#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002586#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002587#define CBQUOTE 6 /* backwards single quote */
2588#define CVAR 7 /* a dollar sign */
2589#define CENDVAR 8 /* a '}' character */
2590#define CLP 9 /* a left paren in arithmetic */
2591#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002592#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002593#define CCTL 12 /* like CWORD, except it must be escaped */
2594#define CSPCL 13 /* these terminate a word */
2595#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002596
Denis Vlasenko131ae172007-02-18 13:00:19 +00002597#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002598#define SYNBASE 130
2599#define PEOF -130
2600#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002601#define PEOA_OR_PEOF PEOA
2602#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002603#define SYNBASE 129
2604#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002605#define PEOA_OR_PEOF PEOF
2606#endif
2607
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002608/* number syntax index */
2609#define BASESYNTAX 0 /* not in quotes */
2610#define DQSYNTAX 1 /* in double quotes */
2611#define SQSYNTAX 2 /* in single quotes */
2612#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002613#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002614
Denis Vlasenko131ae172007-02-18 13:00:19 +00002615#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002616#define USE_SIT_FUNCTION
2617#endif
2618
Denis Vlasenko131ae172007-02-18 13:00:19 +00002619#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002620static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002621#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002622 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002623#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002624 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2625 { CNL, CNL, CNL, CNL }, /* 2, \n */
2626 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2627 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2628 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2629 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2630 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2631 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2632 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2633 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2634 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002635#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002636 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2637 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2638 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002639#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002640};
Eric Andersenc470f442003-07-28 09:56:35 +00002641#else
2642static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002643#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002644 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002645#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002646 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2647 { CNL, CNL, CNL }, /* 2, \n */
2648 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2649 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2650 { CVAR, CVAR, CWORD }, /* 5, $ */
2651 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2652 { CSPCL, CWORD, CWORD }, /* 7, ( */
2653 { CSPCL, CWORD, CWORD }, /* 8, ) */
2654 { CBACK, CBACK, CCTL }, /* 9, \ */
2655 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2656 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002657#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002658 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2659 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2660 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002661#endif
2662};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002663#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002664
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002665#ifdef USE_SIT_FUNCTION
2666
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002667static int
2668SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002669{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002670 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002671#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002672 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002673 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2674 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2675 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2676 11, 3 /* "}~" */
2677 };
2678#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002679 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002680 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2681 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2682 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2683 10, 2 /* "}~" */
2684 };
2685#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002686 const char *s;
2687 int indx;
2688
Eric Andersenc470f442003-07-28 09:56:35 +00002689 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002690 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002691#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002692 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002693 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002694 else
2695#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002696
2697 if ((unsigned char)c >= (unsigned char)(CTLESC)
2698 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2699 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002700 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002701 } else {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002702 s = strchrnul(spec_symbls, c);
2703 if (*s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002704 return CWORD;
Denis Vlasenkoef527f52008-06-23 01:52:30 +00002705 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002706 }
2707 return S_I_T[indx][syntax];
2708}
2709
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002710#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002711
Denis Vlasenko131ae172007-02-18 13:00:19 +00002712#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002713#define CSPCL_CIGN_CIGN_CIGN 0
2714#define CSPCL_CWORD_CWORD_CWORD 1
2715#define CNL_CNL_CNL_CNL 2
2716#define CWORD_CCTL_CCTL_CWORD 3
2717#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2718#define CVAR_CVAR_CWORD_CVAR 5
2719#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2720#define CSPCL_CWORD_CWORD_CLP 7
2721#define CSPCL_CWORD_CWORD_CRP 8
2722#define CBACK_CBACK_CCTL_CBACK 9
2723#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2724#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2725#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2726#define CWORD_CWORD_CWORD_CWORD 13
2727#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002728#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002729#define CSPCL_CWORD_CWORD_CWORD 0
2730#define CNL_CNL_CNL_CNL 1
2731#define CWORD_CCTL_CCTL_CWORD 2
2732#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2733#define CVAR_CVAR_CWORD_CVAR 4
2734#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2735#define CSPCL_CWORD_CWORD_CLP 6
2736#define CSPCL_CWORD_CWORD_CRP 7
2737#define CBACK_CBACK_CCTL_CBACK 8
2738#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2739#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2740#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2741#define CWORD_CWORD_CWORD_CWORD 12
2742#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002743#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002744
2745static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002746 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002747 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002748#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002749 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2750#endif
2751 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2753 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2754 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2755 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2756 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2757 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2758 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2759 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002760 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2889 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2890 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2912 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002913 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002914 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2916 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002918 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002919 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2920 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2921 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2922 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2924 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2925 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2926 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2927 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2938 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2939 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2940 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2942 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2943 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2971 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2972 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2973 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2976 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3004 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3005 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3006 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003007};
3008
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003009#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3010
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003011#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003012
Eric Andersen2870d962001-07-02 17:27:21 +00003013
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003014/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003015
Denis Vlasenko131ae172007-02-18 13:00:19 +00003016#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003017
3018#define ALIASINUSE 1
3019#define ALIASDEAD 2
3020
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003021struct alias {
3022 struct alias *next;
3023 char *name;
3024 char *val;
3025 int flag;
3026};
3027
Denis Vlasenko01631112007-12-16 17:20:38 +00003028
3029static struct alias **atab; // [ATABSIZE];
3030#define INIT_G_alias() do { \
3031 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3032} while (0)
3033
Eric Andersen2870d962001-07-02 17:27:21 +00003034
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003035static struct alias **
3036__lookupalias(const char *name) {
3037 unsigned int hashval;
3038 struct alias **app;
3039 const char *p;
3040 unsigned int ch;
3041
3042 p = name;
3043
3044 ch = (unsigned char)*p;
3045 hashval = ch << 4;
3046 while (ch) {
3047 hashval += ch;
3048 ch = (unsigned char)*++p;
3049 }
3050 app = &atab[hashval % ATABSIZE];
3051
3052 for (; *app; app = &(*app)->next) {
3053 if (strcmp(name, (*app)->name) == 0) {
3054 break;
3055 }
3056 }
3057
3058 return app;
3059}
3060
3061static struct alias *
3062lookupalias(const char *name, int check)
3063{
3064 struct alias *ap = *__lookupalias(name);
3065
3066 if (check && ap && (ap->flag & ALIASINUSE))
3067 return NULL;
3068 return ap;
3069}
3070
3071static struct alias *
3072freealias(struct alias *ap)
3073{
3074 struct alias *next;
3075
3076 if (ap->flag & ALIASINUSE) {
3077 ap->flag |= ALIASDEAD;
3078 return ap;
3079 }
3080
3081 next = ap->next;
3082 free(ap->name);
3083 free(ap->val);
3084 free(ap);
3085 return next;
3086}
Eric Andersencb57d552001-06-28 07:25:16 +00003087
Eric Andersenc470f442003-07-28 09:56:35 +00003088static void
3089setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003090{
3091 struct alias *ap, **app;
3092
3093 app = __lookupalias(name);
3094 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003095 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003096 if (ap) {
3097 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003098 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003099 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003100 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003101 ap->flag &= ~ALIASDEAD;
3102 } else {
3103 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003104 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003105 ap->name = ckstrdup(name);
3106 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003107 /*ap->flag = 0; - ckzalloc did it */
3108 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003109 *app = ap;
3110 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003111 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003112}
3113
Eric Andersenc470f442003-07-28 09:56:35 +00003114static int
3115unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003116{
Eric Andersencb57d552001-06-28 07:25:16 +00003117 struct alias **app;
3118
3119 app = __lookupalias(name);
3120
3121 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003122 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003123 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003124 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003125 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003126 }
3127
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003128 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003129}
3130
Eric Andersenc470f442003-07-28 09:56:35 +00003131static void
3132rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003133{
Eric Andersencb57d552001-06-28 07:25:16 +00003134 struct alias *ap, **app;
3135 int i;
3136
Denis Vlasenkob012b102007-02-19 22:43:01 +00003137 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003138 for (i = 0; i < ATABSIZE; i++) {
3139 app = &atab[i];
3140 for (ap = *app; ap; ap = *app) {
3141 *app = freealias(*app);
3142 if (ap == *app) {
3143 app = &ap->next;
3144 }
3145 }
3146 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003147 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003148}
3149
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003150static void
3151printalias(const struct alias *ap)
3152{
3153 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3154}
3155
Eric Andersencb57d552001-06-28 07:25:16 +00003156/*
3157 * TODO - sort output
3158 */
Eric Andersenc470f442003-07-28 09:56:35 +00003159static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003160aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003161{
3162 char *n, *v;
3163 int ret = 0;
3164 struct alias *ap;
3165
Denis Vlasenko68404f12008-03-17 09:00:54 +00003166 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003167 int i;
3168
Denis Vlasenko68404f12008-03-17 09:00:54 +00003169 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003170 for (ap = atab[i]; ap; ap = ap->next) {
3171 printalias(ap);
3172 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003173 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003174 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003175 }
3176 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003177 v = strchr(n+1, '=');
3178 if (v == NULL) { /* n+1: funny ksh stuff */
3179 ap = *__lookupalias(n);
3180 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003181 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003182 ret = 1;
3183 } else
3184 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003185 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003186 *v++ = '\0';
3187 setalias(n, v);
3188 }
3189 }
3190
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003191 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003192}
3193
Eric Andersenc470f442003-07-28 09:56:35 +00003194static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003195unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003196{
3197 int i;
3198
3199 while ((i = nextopt("a")) != '\0') {
3200 if (i == 'a') {
3201 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003202 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003203 }
3204 }
3205 for (i = 0; *argptr; argptr++) {
3206 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003207 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003208 i = 1;
3209 }
3210 }
3211
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003212 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003213}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003214
Denis Vlasenko131ae172007-02-18 13:00:19 +00003215#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003216
Eric Andersenc470f442003-07-28 09:56:35 +00003217
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003218/* ============ jobs.c */
3219
3220/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3221#define FORK_FG 0
3222#define FORK_BG 1
3223#define FORK_NOJOB 2
3224
3225/* mode flags for showjob(s) */
3226#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3227#define SHOW_PID 0x04 /* include process pid */
3228#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3229
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003230/*
3231 * A job structure contains information about a job. A job is either a
3232 * single process or a set of processes contained in a pipeline. In the
3233 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3234 * array of pids.
3235 */
3236
3237struct procstat {
3238 pid_t pid; /* process id */
3239 int status; /* last process status from wait() */
3240 char *cmd; /* text of command being run */
3241};
3242
3243struct job {
3244 struct procstat ps0; /* status of process */
3245 struct procstat *ps; /* status or processes when more than one */
3246#if JOBS
3247 int stopstatus; /* status of a stopped job */
3248#endif
3249 uint32_t
3250 nprocs: 16, /* number of processes */
3251 state: 8,
3252#define JOBRUNNING 0 /* at least one proc running */
3253#define JOBSTOPPED 1 /* all procs are stopped */
3254#define JOBDONE 2 /* all procs are completed */
3255#if JOBS
3256 sigint: 1, /* job was killed by SIGINT */
3257 jobctl: 1, /* job running under job control */
3258#endif
3259 waited: 1, /* true if this entry has been waited for */
3260 used: 1, /* true if this entry is in used */
3261 changed: 1; /* true if status has changed */
3262 struct job *prev_job; /* previous job */
3263};
3264
Denis Vlasenko68404f12008-03-17 09:00:54 +00003265static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003266#if !JOBS
3267#define forkshell(job, node, mode) forkshell(job, mode)
3268#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003269static int forkshell(struct job *, union node *, int);
3270static int waitforjob(struct job *);
3271
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003272#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003273enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003274#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003275#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003276static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003277static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003278#endif
3279
3280/*
3281 * Set the signal handler for the specified signal. The routine figures
3282 * out what it should be set to.
3283 */
3284static void
3285setsignal(int signo)
3286{
3287 int action;
3288 char *t, tsig;
3289 struct sigaction act;
3290
3291 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003292 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003293 if (t == NULL)
3294 action = S_DFL;
3295 else if (*t != '\0')
3296 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003297 if (rootshell && action == S_DFL) {
3298 switch (signo) {
3299 case SIGINT:
3300 if (iflag || minusc || sflag == 0)
3301 action = S_CATCH;
3302 break;
3303 case SIGQUIT:
3304#if DEBUG
3305 if (debug)
3306 break;
3307#endif
3308 /* FALLTHROUGH */
3309 case SIGTERM:
3310 if (iflag)
3311 action = S_IGN;
3312 break;
3313#if JOBS
3314 case SIGTSTP:
3315 case SIGTTOU:
3316 if (mflag)
3317 action = S_IGN;
3318 break;
3319#endif
3320 }
3321 }
3322
3323 t = &sigmode[signo - 1];
3324 tsig = *t;
3325 if (tsig == 0) {
3326 /*
3327 * current setting unknown
3328 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003329 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003330 /*
3331 * Pretend it worked; maybe we should give a warning
3332 * here, but other shells don't. We don't alter
3333 * sigmode, so that we retry every time.
3334 */
3335 return;
3336 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003337 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003339 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003340 if (mflag
3341 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3342 ) {
3343 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003344 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003345 }
3346 }
3347 if (tsig == S_HARD_IGN || tsig == action)
3348 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003349 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003350 switch (action) {
3351 case S_CATCH:
3352 act.sa_handler = onsig;
3353 break;
3354 case S_IGN:
3355 act.sa_handler = SIG_IGN;
3356 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003357 }
3358 *t = action;
3359 act.sa_flags = 0;
3360 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003361 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362}
3363
3364/* mode flags for set_curjob */
3365#define CUR_DELETE 2
3366#define CUR_RUNNING 1
3367#define CUR_STOPPED 0
3368
3369/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003370#define DOWAIT_NONBLOCK WNOHANG
3371#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003372
3373#if JOBS
3374/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003375static int initialpgrp; //references:2
3376static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003377#endif
3378/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003379static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003380/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003381static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003382/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003383static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003384/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003385static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003386
3387static void
3388set_curjob(struct job *jp, unsigned mode)
3389{
3390 struct job *jp1;
3391 struct job **jpp, **curp;
3392
3393 /* first remove from list */
3394 jpp = curp = &curjob;
3395 do {
3396 jp1 = *jpp;
3397 if (jp1 == jp)
3398 break;
3399 jpp = &jp1->prev_job;
3400 } while (1);
3401 *jpp = jp1->prev_job;
3402
3403 /* Then re-insert in correct position */
3404 jpp = curp;
3405 switch (mode) {
3406 default:
3407#if DEBUG
3408 abort();
3409#endif
3410 case CUR_DELETE:
3411 /* job being deleted */
3412 break;
3413 case CUR_RUNNING:
3414 /* newly created job or backgrounded job,
3415 put after all stopped jobs. */
3416 do {
3417 jp1 = *jpp;
3418#if JOBS
3419 if (!jp1 || jp1->state != JOBSTOPPED)
3420#endif
3421 break;
3422 jpp = &jp1->prev_job;
3423 } while (1);
3424 /* FALLTHROUGH */
3425#if JOBS
3426 case CUR_STOPPED:
3427#endif
3428 /* newly stopped job - becomes curjob */
3429 jp->prev_job = *jpp;
3430 *jpp = jp;
3431 break;
3432 }
3433}
3434
3435#if JOBS || DEBUG
3436static int
3437jobno(const struct job *jp)
3438{
3439 return jp - jobtab + 1;
3440}
3441#endif
3442
3443/*
3444 * Convert a job name to a job structure.
3445 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003446#if !JOBS
3447#define getjob(name, getctl) getjob(name)
3448#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003449static struct job *
3450getjob(const char *name, int getctl)
3451{
3452 struct job *jp;
3453 struct job *found;
3454 const char *err_msg = "No such job: %s";
3455 unsigned num;
3456 int c;
3457 const char *p;
3458 char *(*match)(const char *, const char *);
3459
3460 jp = curjob;
3461 p = name;
3462 if (!p)
3463 goto currentjob;
3464
3465 if (*p != '%')
3466 goto err;
3467
3468 c = *++p;
3469 if (!c)
3470 goto currentjob;
3471
3472 if (!p[1]) {
3473 if (c == '+' || c == '%') {
3474 currentjob:
3475 err_msg = "No current job";
3476 goto check;
3477 }
3478 if (c == '-') {
3479 if (jp)
3480 jp = jp->prev_job;
3481 err_msg = "No previous job";
3482 check:
3483 if (!jp)
3484 goto err;
3485 goto gotit;
3486 }
3487 }
3488
3489 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003490// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003491 num = atoi(p);
3492 if (num < njobs) {
3493 jp = jobtab + num - 1;
3494 if (jp->used)
3495 goto gotit;
3496 goto err;
3497 }
3498 }
3499
3500 match = prefix;
3501 if (*p == '?') {
3502 match = strstr;
3503 p++;
3504 }
3505
3506 found = 0;
3507 while (1) {
3508 if (!jp)
3509 goto err;
3510 if (match(jp->ps[0].cmd, p)) {
3511 if (found)
3512 goto err;
3513 found = jp;
3514 err_msg = "%s: ambiguous";
3515 }
3516 jp = jp->prev_job;
3517 }
3518
3519 gotit:
3520#if JOBS
3521 err_msg = "job %s not created under job control";
3522 if (getctl && jp->jobctl == 0)
3523 goto err;
3524#endif
3525 return jp;
3526 err:
3527 ash_msg_and_raise_error(err_msg, name);
3528}
3529
3530/*
3531 * Mark a job structure as unused.
3532 */
3533static void
3534freejob(struct job *jp)
3535{
3536 struct procstat *ps;
3537 int i;
3538
3539 INT_OFF;
3540 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3541 if (ps->cmd != nullstr)
3542 free(ps->cmd);
3543 }
3544 if (jp->ps != &jp->ps0)
3545 free(jp->ps);
3546 jp->used = 0;
3547 set_curjob(jp, CUR_DELETE);
3548 INT_ON;
3549}
3550
3551#if JOBS
3552static void
3553xtcsetpgrp(int fd, pid_t pgrp)
3554{
3555 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003556 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003557}
3558
3559/*
3560 * Turn job control on and off.
3561 *
3562 * Note: This code assumes that the third arg to ioctl is a character
3563 * pointer, which is true on Berkeley systems but not System V. Since
3564 * System V doesn't have job control yet, this isn't a problem now.
3565 *
3566 * Called with interrupts off.
3567 */
3568static void
3569setjobctl(int on)
3570{
3571 int fd;
3572 int pgrp;
3573
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003574 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003575 return;
3576 if (on) {
3577 int ofd;
3578 ofd = fd = open(_PATH_TTY, O_RDWR);
3579 if (fd < 0) {
3580 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3581 * That sometimes helps to acquire controlling tty.
3582 * Obviously, a workaround for bugs when someone
3583 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003584 fd = 2;
3585 while (!isatty(fd))
3586 if (--fd < 0)
3587 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003588 }
3589 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003590 if (ofd >= 0)
3591 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003592 if (fd < 0)
3593 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003594 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003595 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003596 do { /* while we are in the background */
3597 pgrp = tcgetpgrp(fd);
3598 if (pgrp < 0) {
3599 out:
3600 ash_msg("can't access tty; job control turned off");
3601 mflag = on = 0;
3602 goto close;
3603 }
3604 if (pgrp == getpgrp())
3605 break;
3606 killpg(0, SIGTTIN);
3607 } while (1);
3608 initialpgrp = pgrp;
3609
3610 setsignal(SIGTSTP);
3611 setsignal(SIGTTOU);
3612 setsignal(SIGTTIN);
3613 pgrp = rootpid;
3614 setpgid(0, pgrp);
3615 xtcsetpgrp(fd, pgrp);
3616 } else {
3617 /* turning job control off */
3618 fd = ttyfd;
3619 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003620 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003621 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003622 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003623 setpgid(0, pgrp);
3624 setsignal(SIGTSTP);
3625 setsignal(SIGTTOU);
3626 setsignal(SIGTTIN);
3627 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003628 if (fd >= 0)
3629 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003630 fd = -1;
3631 }
3632 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003633 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634}
3635
3636static int
3637killcmd(int argc, char **argv)
3638{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003639 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003640 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003641 do {
3642 if (argv[i][0] == '%') {
3643 struct job *jp = getjob(argv[i], 0);
3644 unsigned pid = jp->ps[0].pid;
3645 /* Enough space for ' -NNN<nul>' */
3646 argv[i] = alloca(sizeof(int)*3 + 3);
3647 /* kill_main has matching code to expect
3648 * leading space. Needed to not confuse
3649 * negative pids with "kill -SIGNAL_NO" syntax */
3650 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003651 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003652 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003653 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003654 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003655}
3656
3657static void
3658showpipe(struct job *jp, FILE *out)
3659{
3660 struct procstat *sp;
3661 struct procstat *spend;
3662
3663 spend = jp->ps + jp->nprocs;
3664 for (sp = jp->ps + 1; sp < spend; sp++)
3665 fprintf(out, " | %s", sp->cmd);
3666 outcslow('\n', out);
3667 flush_stdout_stderr();
3668}
3669
3670
3671static int
3672restartjob(struct job *jp, int mode)
3673{
3674 struct procstat *ps;
3675 int i;
3676 int status;
3677 pid_t pgid;
3678
3679 INT_OFF;
3680 if (jp->state == JOBDONE)
3681 goto out;
3682 jp->state = JOBRUNNING;
3683 pgid = jp->ps->pid;
3684 if (mode == FORK_FG)
3685 xtcsetpgrp(ttyfd, pgid);
3686 killpg(pgid, SIGCONT);
3687 ps = jp->ps;
3688 i = jp->nprocs;
3689 do {
3690 if (WIFSTOPPED(ps->status)) {
3691 ps->status = -1;
3692 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003693 ps++;
3694 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003695 out:
3696 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3697 INT_ON;
3698 return status;
3699}
3700
3701static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003702fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003703{
3704 struct job *jp;
3705 FILE *out;
3706 int mode;
3707 int retval;
3708
3709 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3710 nextopt(nullstr);
3711 argv = argptr;
3712 out = stdout;
3713 do {
3714 jp = getjob(*argv, 1);
3715 if (mode == FORK_BG) {
3716 set_curjob(jp, CUR_RUNNING);
3717 fprintf(out, "[%d] ", jobno(jp));
3718 }
3719 outstr(jp->ps->cmd, out);
3720 showpipe(jp, out);
3721 retval = restartjob(jp, mode);
3722 } while (*argv && *++argv);
3723 return retval;
3724}
3725#endif
3726
3727static int
3728sprint_status(char *s, int status, int sigonly)
3729{
3730 int col;
3731 int st;
3732
3733 col = 0;
3734 if (!WIFEXITED(status)) {
3735#if JOBS
3736 if (WIFSTOPPED(status))
3737 st = WSTOPSIG(status);
3738 else
3739#endif
3740 st = WTERMSIG(status);
3741 if (sigonly) {
3742 if (st == SIGINT || st == SIGPIPE)
3743 goto out;
3744#if JOBS
3745 if (WIFSTOPPED(status))
3746 goto out;
3747#endif
3748 }
3749 st &= 0x7f;
3750 col = fmtstr(s, 32, strsignal(st));
3751 if (WCOREDUMP(status)) {
3752 col += fmtstr(s + col, 16, " (core dumped)");
3753 }
3754 } else if (!sigonly) {
3755 st = WEXITSTATUS(status);
3756 if (st)
3757 col = fmtstr(s, 16, "Done(%d)", st);
3758 else
3759 col = fmtstr(s, 16, "Done");
3760 }
3761 out:
3762 return col;
3763}
3764
3765/*
3766 * Do a wait system call. If job control is compiled in, we accept
3767 * stopped processes. If block is zero, we return a value of zero
3768 * rather than blocking.
3769 *
3770 * System V doesn't have a non-blocking wait system call. It does
3771 * have a SIGCLD signal that is sent to a process when one of it's
3772 * children dies. The obvious way to use SIGCLD would be to install
3773 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3774 * was received, and have waitproc bump another counter when it got
3775 * the status of a process. Waitproc would then know that a wait
3776 * system call would not block if the two counters were different.
3777 * This approach doesn't work because if a process has children that
3778 * have not been waited for, System V will send it a SIGCLD when it
3779 * installs a signal handler for SIGCLD. What this means is that when
3780 * a child exits, the shell will be sent SIGCLD signals continuously
3781 * until is runs out of stack space, unless it does a wait call before
3782 * restoring the signal handler. The code below takes advantage of
3783 * this (mis)feature by installing a signal handler for SIGCLD and
3784 * then checking to see whether it was called. If there are any
3785 * children to be waited for, it will be.
3786 *
3787 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3788 * waits at all. In this case, the user will not be informed when
3789 * a background process until the next time she runs a real program
3790 * (as opposed to running a builtin command or just typing return),
3791 * and the jobs command may give out of date information.
3792 */
3793static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003794waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003797 if (doing_jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003798 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003799#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003800 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3801 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802}
3803
3804/*
3805 * Wait for a process to terminate.
3806 */
3807static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003808dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003809{
3810 int pid;
3811 int status;
3812 struct job *jp;
3813 struct job *thisjob;
3814 int state;
3815
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003816 TRACE(("dowait(%d) called\n", wait_flags));
3817 pid = waitproc(wait_flags, &status);
3818 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003819 if (pid <= 0) {
3820 /* If we were doing blocking wait and (probably) got EINTR,
3821 * check for pending sigs received while waiting.
3822 * (NB: can be moved into callers if needed) */
3823 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3824 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003825 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003826 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003827 INT_OFF;
3828 thisjob = NULL;
3829 for (jp = curjob; jp; jp = jp->prev_job) {
3830 struct procstat *sp;
3831 struct procstat *spend;
3832 if (jp->state == JOBDONE)
3833 continue;
3834 state = JOBDONE;
3835 spend = jp->ps + jp->nprocs;
3836 sp = jp->ps;
3837 do {
3838 if (sp->pid == pid) {
3839 TRACE(("Job %d: changing status of proc %d "
3840 "from 0x%x to 0x%x\n",
3841 jobno(jp), pid, sp->status, status));
3842 sp->status = status;
3843 thisjob = jp;
3844 }
3845 if (sp->status == -1)
3846 state = JOBRUNNING;
3847#if JOBS
3848 if (state == JOBRUNNING)
3849 continue;
3850 if (WIFSTOPPED(sp->status)) {
3851 jp->stopstatus = sp->status;
3852 state = JOBSTOPPED;
3853 }
3854#endif
3855 } while (++sp < spend);
3856 if (thisjob)
3857 goto gotjob;
3858 }
3859#if JOBS
3860 if (!WIFSTOPPED(status))
3861#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003862 jobless--;
3863 goto out;
3864
3865 gotjob:
3866 if (state != JOBRUNNING) {
3867 thisjob->changed = 1;
3868
3869 if (thisjob->state != state) {
3870 TRACE(("Job %d: changing state from %d to %d\n",
3871 jobno(thisjob), thisjob->state, state));
3872 thisjob->state = state;
3873#if JOBS
3874 if (state == JOBSTOPPED) {
3875 set_curjob(thisjob, CUR_STOPPED);
3876 }
3877#endif
3878 }
3879 }
3880
3881 out:
3882 INT_ON;
3883
3884 if (thisjob && thisjob == job) {
3885 char s[48 + 1];
3886 int len;
3887
3888 len = sprint_status(s, status, 1);
3889 if (len) {
3890 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003891 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003892 out2str(s);
3893 }
3894 }
3895 return pid;
3896}
3897
3898#if JOBS
3899static void
3900showjob(FILE *out, struct job *jp, int mode)
3901{
3902 struct procstat *ps;
3903 struct procstat *psend;
3904 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003905 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003906 char s[80];
3907
3908 ps = jp->ps;
3909
3910 if (mode & SHOW_PGID) {
3911 /* just output process (group) id of pipeline */
3912 fprintf(out, "%d\n", ps->pid);
3913 return;
3914 }
3915
3916 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003917 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003918
3919 if (jp == curjob)
3920 s[col - 2] = '+';
3921 else if (curjob && jp == curjob->prev_job)
3922 s[col - 2] = '-';
3923
3924 if (mode & SHOW_PID)
3925 col += fmtstr(s + col, 16, "%d ", ps->pid);
3926
3927 psend = ps + jp->nprocs;
3928
3929 if (jp->state == JOBRUNNING) {
3930 strcpy(s + col, "Running");
3931 col += sizeof("Running") - 1;
3932 } else {
3933 int status = psend[-1].status;
3934 if (jp->state == JOBSTOPPED)
3935 status = jp->stopstatus;
3936 col += sprint_status(s + col, status, 0);
3937 }
3938
3939 goto start;
3940
3941 do {
3942 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003943 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944 start:
3945 fprintf(out, "%s%*c%s",
3946 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3947 );
3948 if (!(mode & SHOW_PID)) {
3949 showpipe(jp, out);
3950 break;
3951 }
3952 if (++ps == psend) {
3953 outcslow('\n', out);
3954 break;
3955 }
3956 } while (1);
3957
3958 jp->changed = 0;
3959
3960 if (jp->state == JOBDONE) {
3961 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3962 freejob(jp);
3963 }
3964}
3965
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966/*
3967 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3968 * statuses have changed since the last call to showjobs.
3969 */
3970static void
3971showjobs(FILE *out, int mode)
3972{
3973 struct job *jp;
3974
3975 TRACE(("showjobs(%x) called\n", mode));
3976
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003977 /* If not even one job changed, there is nothing to do */
3978 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003979 continue;
3980
3981 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003982 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003983 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003984 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003985 }
3986}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003987
3988static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003989jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003990{
3991 int mode, m;
3992
3993 mode = 0;
3994 while ((m = nextopt("lp"))) {
3995 if (m == 'l')
3996 mode = SHOW_PID;
3997 else
3998 mode = SHOW_PGID;
3999 }
4000
4001 argv = argptr;
4002 if (*argv) {
4003 do
4004 showjob(stdout, getjob(*argv,0), mode);
4005 while (*++argv);
4006 } else
4007 showjobs(stdout, mode);
4008
4009 return 0;
4010}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004011#endif /* JOBS */
4012
4013static int
4014getstatus(struct job *job)
4015{
4016 int status;
4017 int retval;
4018
4019 status = job->ps[job->nprocs - 1].status;
4020 retval = WEXITSTATUS(status);
4021 if (!WIFEXITED(status)) {
4022#if JOBS
4023 retval = WSTOPSIG(status);
4024 if (!WIFSTOPPED(status))
4025#endif
4026 {
4027 /* XXX: limits number of signals */
4028 retval = WTERMSIG(status);
4029#if JOBS
4030 if (retval == SIGINT)
4031 job->sigint = 1;
4032#endif
4033 }
4034 retval += 128;
4035 }
4036 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4037 jobno(job), job->nprocs, status, retval));
4038 return retval;
4039}
4040
4041static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004042waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043{
4044 struct job *job;
4045 int retval;
4046 struct job *jp;
4047
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004048// exsig++;
4049// xbarrier();
4050 if (pendingsig)
4051 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052
4053 nextopt(nullstr);
4054 retval = 0;
4055
4056 argv = argptr;
4057 if (!*argv) {
4058 /* wait for all jobs */
4059 for (;;) {
4060 jp = curjob;
4061 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004062 if (!jp) /* no running procs */
4063 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064 if (jp->state == JOBRUNNING)
4065 break;
4066 jp->waited = 1;
4067 jp = jp->prev_job;
4068 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004069 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004070 }
4071 }
4072
4073 retval = 127;
4074 do {
4075 if (**argv != '%') {
4076 pid_t pid = number(*argv);
4077 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004078 while (1) {
4079 if (!job)
4080 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 if (job->ps[job->nprocs - 1].pid == pid)
4082 break;
4083 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004084 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004085 } else
4086 job = getjob(*argv, 0);
4087 /* loop until process terminated or stopped */
4088 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004089 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004090 job->waited = 1;
4091 retval = getstatus(job);
4092 repeat:
4093 ;
4094 } while (*++argv);
4095
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004096 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004097 return retval;
4098}
4099
4100static struct job *
4101growjobtab(void)
4102{
4103 size_t len;
4104 ptrdiff_t offset;
4105 struct job *jp, *jq;
4106
4107 len = njobs * sizeof(*jp);
4108 jq = jobtab;
4109 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4110
4111 offset = (char *)jp - (char *)jq;
4112 if (offset) {
4113 /* Relocate pointers */
4114 size_t l = len;
4115
4116 jq = (struct job *)((char *)jq + l);
4117 while (l) {
4118 l -= sizeof(*jp);
4119 jq--;
4120#define joff(p) ((struct job *)((char *)(p) + l))
4121#define jmove(p) (p) = (void *)((char *)(p) + offset)
4122 if (joff(jp)->ps == &jq->ps0)
4123 jmove(joff(jp)->ps);
4124 if (joff(jp)->prev_job)
4125 jmove(joff(jp)->prev_job);
4126 }
4127 if (curjob)
4128 jmove(curjob);
4129#undef joff
4130#undef jmove
4131 }
4132
4133 njobs += 4;
4134 jobtab = jp;
4135 jp = (struct job *)((char *)jp + len);
4136 jq = jp + 3;
4137 do {
4138 jq->used = 0;
4139 } while (--jq >= jp);
4140 return jp;
4141}
4142
4143/*
4144 * Return a new job structure.
4145 * Called with interrupts off.
4146 */
4147static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004148makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004149{
4150 int i;
4151 struct job *jp;
4152
4153 for (i = njobs, jp = jobtab; ; jp++) {
4154 if (--i < 0) {
4155 jp = growjobtab();
4156 break;
4157 }
4158 if (jp->used == 0)
4159 break;
4160 if (jp->state != JOBDONE || !jp->waited)
4161 continue;
4162#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004163 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004164 continue;
4165#endif
4166 freejob(jp);
4167 break;
4168 }
4169 memset(jp, 0, sizeof(*jp));
4170#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004171 /* jp->jobctl is a bitfield.
4172 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004173 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004174 jp->jobctl = 1;
4175#endif
4176 jp->prev_job = curjob;
4177 curjob = jp;
4178 jp->used = 1;
4179 jp->ps = &jp->ps0;
4180 if (nprocs > 1) {
4181 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4182 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004183 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184 jobno(jp)));
4185 return jp;
4186}
4187
4188#if JOBS
4189/*
4190 * Return a string identifying a command (to be printed by the
4191 * jobs command).
4192 */
4193static char *cmdnextc;
4194
4195static void
4196cmdputs(const char *s)
4197{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004198 static const char vstype[VSTYPE + 1][3] = {
4199 "", "}", "-", "+", "?", "=",
4200 "%", "%%", "#", "##"
4201 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4202 };
4203
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204 const char *p, *str;
4205 char c, cc[2] = " ";
4206 char *nextc;
4207 int subtype = 0;
4208 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004209
4210 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4211 p = s;
4212 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004213 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214 switch (c) {
4215 case CTLESC:
4216 c = *p++;
4217 break;
4218 case CTLVAR:
4219 subtype = *p++;
4220 if ((subtype & VSTYPE) == VSLENGTH)
4221 str = "${#";
4222 else
4223 str = "${";
4224 if (!(subtype & VSQUOTE) == !(quoted & 1))
4225 goto dostr;
4226 quoted ^= 1;
4227 c = '"';
4228 break;
4229 case CTLENDVAR:
4230 str = "\"}" + !(quoted & 1);
4231 quoted >>= 1;
4232 subtype = 0;
4233 goto dostr;
4234 case CTLBACKQ:
4235 str = "$(...)";
4236 goto dostr;
4237 case CTLBACKQ+CTLQUOTE:
4238 str = "\"$(...)\"";
4239 goto dostr;
4240#if ENABLE_ASH_MATH_SUPPORT
4241 case CTLARI:
4242 str = "$((";
4243 goto dostr;
4244 case CTLENDARI:
4245 str = "))";
4246 goto dostr;
4247#endif
4248 case CTLQUOTEMARK:
4249 quoted ^= 1;
4250 c = '"';
4251 break;
4252 case '=':
4253 if (subtype == 0)
4254 break;
4255 if ((subtype & VSTYPE) != VSNORMAL)
4256 quoted <<= 1;
4257 str = vstype[subtype & VSTYPE];
4258 if (subtype & VSNUL)
4259 c = ':';
4260 else
4261 goto checkstr;
4262 break;
4263 case '\'':
4264 case '\\':
4265 case '"':
4266 case '$':
4267 /* These can only happen inside quotes */
4268 cc[0] = c;
4269 str = cc;
4270 c = '\\';
4271 break;
4272 default:
4273 break;
4274 }
4275 USTPUTC(c, nextc);
4276 checkstr:
4277 if (!str)
4278 continue;
4279 dostr:
4280 while ((c = *str++)) {
4281 USTPUTC(c, nextc);
4282 }
4283 }
4284 if (quoted & 1) {
4285 USTPUTC('"', nextc);
4286 }
4287 *nextc = 0;
4288 cmdnextc = nextc;
4289}
4290
4291/* cmdtxt() and cmdlist() call each other */
4292static void cmdtxt(union node *n);
4293
4294static void
4295cmdlist(union node *np, int sep)
4296{
4297 for (; np; np = np->narg.next) {
4298 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004299 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004300 cmdtxt(np);
4301 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004302 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004303 }
4304}
4305
4306static void
4307cmdtxt(union node *n)
4308{
4309 union node *np;
4310 struct nodelist *lp;
4311 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004312
4313 if (!n)
4314 return;
4315 switch (n->type) {
4316 default:
4317#if DEBUG
4318 abort();
4319#endif
4320 case NPIPE:
4321 lp = n->npipe.cmdlist;
4322 for (;;) {
4323 cmdtxt(lp->n);
4324 lp = lp->next;
4325 if (!lp)
4326 break;
4327 cmdputs(" | ");
4328 }
4329 break;
4330 case NSEMI:
4331 p = "; ";
4332 goto binop;
4333 case NAND:
4334 p = " && ";
4335 goto binop;
4336 case NOR:
4337 p = " || ";
4338 binop:
4339 cmdtxt(n->nbinary.ch1);
4340 cmdputs(p);
4341 n = n->nbinary.ch2;
4342 goto donode;
4343 case NREDIR:
4344 case NBACKGND:
4345 n = n->nredir.n;
4346 goto donode;
4347 case NNOT:
4348 cmdputs("!");
4349 n = n->nnot.com;
4350 donode:
4351 cmdtxt(n);
4352 break;
4353 case NIF:
4354 cmdputs("if ");
4355 cmdtxt(n->nif.test);
4356 cmdputs("; then ");
4357 n = n->nif.ifpart;
4358 if (n->nif.elsepart) {
4359 cmdtxt(n);
4360 cmdputs("; else ");
4361 n = n->nif.elsepart;
4362 }
4363 p = "; fi";
4364 goto dotail;
4365 case NSUBSHELL:
4366 cmdputs("(");
4367 n = n->nredir.n;
4368 p = ")";
4369 goto dotail;
4370 case NWHILE:
4371 p = "while ";
4372 goto until;
4373 case NUNTIL:
4374 p = "until ";
4375 until:
4376 cmdputs(p);
4377 cmdtxt(n->nbinary.ch1);
4378 n = n->nbinary.ch2;
4379 p = "; done";
4380 dodo:
4381 cmdputs("; do ");
4382 dotail:
4383 cmdtxt(n);
4384 goto dotail2;
4385 case NFOR:
4386 cmdputs("for ");
4387 cmdputs(n->nfor.var);
4388 cmdputs(" in ");
4389 cmdlist(n->nfor.args, 1);
4390 n = n->nfor.body;
4391 p = "; done";
4392 goto dodo;
4393 case NDEFUN:
4394 cmdputs(n->narg.text);
4395 p = "() { ... }";
4396 goto dotail2;
4397 case NCMD:
4398 cmdlist(n->ncmd.args, 1);
4399 cmdlist(n->ncmd.redirect, 0);
4400 break;
4401 case NARG:
4402 p = n->narg.text;
4403 dotail2:
4404 cmdputs(p);
4405 break;
4406 case NHERE:
4407 case NXHERE:
4408 p = "<<...";
4409 goto dotail2;
4410 case NCASE:
4411 cmdputs("case ");
4412 cmdputs(n->ncase.expr->narg.text);
4413 cmdputs(" in ");
4414 for (np = n->ncase.cases; np; np = np->nclist.next) {
4415 cmdtxt(np->nclist.pattern);
4416 cmdputs(") ");
4417 cmdtxt(np->nclist.body);
4418 cmdputs(";; ");
4419 }
4420 p = "esac";
4421 goto dotail2;
4422 case NTO:
4423 p = ">";
4424 goto redir;
4425 case NCLOBBER:
4426 p = ">|";
4427 goto redir;
4428 case NAPPEND:
4429 p = ">>";
4430 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004431#if ENABLE_ASH_BASH_COMPAT
4432 case NTO2:
4433#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004434 case NTOFD:
4435 p = ">&";
4436 goto redir;
4437 case NFROM:
4438 p = "<";
4439 goto redir;
4440 case NFROMFD:
4441 p = "<&";
4442 goto redir;
4443 case NFROMTO:
4444 p = "<>";
4445 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004446 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004447 cmdputs(p);
4448 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004449 cmdputs(utoa(n->ndup.dupfd));
4450 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004451 }
4452 n = n->nfile.fname;
4453 goto donode;
4454 }
4455}
4456
4457static char *
4458commandtext(union node *n)
4459{
4460 char *name;
4461
4462 STARTSTACKSTR(cmdnextc);
4463 cmdtxt(n);
4464 name = stackblock();
4465 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4466 name, cmdnextc, cmdnextc));
4467 return ckstrdup(name);
4468}
4469#endif /* JOBS */
4470
4471/*
4472 * Fork off a subshell. If we are doing job control, give the subshell its
4473 * own process group. Jp is a job structure that the job is to be added to.
4474 * N is the command that will be evaluated by the child. Both jp and n may
4475 * be NULL. The mode parameter can be one of the following:
4476 * FORK_FG - Fork off a foreground process.
4477 * FORK_BG - Fork off a background process.
4478 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4479 * process group even if job control is on.
4480 *
4481 * When job control is turned off, background processes have their standard
4482 * input redirected to /dev/null (except for the second and later processes
4483 * in a pipeline).
4484 *
4485 * Called with interrupts off.
4486 */
4487/*
4488 * Clear traps on a fork.
4489 */
4490static void
4491clear_traps(void)
4492{
4493 char **tp;
4494
4495 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004496 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004497 INT_OFF;
4498 free(*tp);
4499 *tp = NULL;
4500 if (tp != &trap[0])
4501 setsignal(tp - trap);
4502 INT_ON;
4503 }
4504 }
4505}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004506
4507/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004508static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004509
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004510/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004511static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004512forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004513{
4514 int oldlvl;
4515
4516 TRACE(("Child shell %d\n", getpid()));
4517 oldlvl = shlvl;
4518 shlvl++;
4519
4520 closescript();
4521 clear_traps();
4522#if JOBS
4523 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004524 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004525 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4526 pid_t pgrp;
4527
4528 if (jp->nprocs == 0)
4529 pgrp = getpid();
4530 else
4531 pgrp = jp->ps[0].pid;
4532 /* This can fail because we are doing it in the parent also */
4533 (void)setpgid(0, pgrp);
4534 if (mode == FORK_FG)
4535 xtcsetpgrp(ttyfd, pgrp);
4536 setsignal(SIGTSTP);
4537 setsignal(SIGTTOU);
4538 } else
4539#endif
4540 if (mode == FORK_BG) {
4541 ignoresig(SIGINT);
4542 ignoresig(SIGQUIT);
4543 if (jp->nprocs == 0) {
4544 close(0);
4545 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004546 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004547 }
4548 }
4549 if (!oldlvl && iflag) {
4550 setsignal(SIGINT);
4551 setsignal(SIGQUIT);
4552 setsignal(SIGTERM);
4553 }
4554 for (jp = curjob; jp; jp = jp->prev_job)
4555 freejob(jp);
4556 jobless = 0;
4557}
4558
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004559/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004560#if !JOBS
4561#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4562#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004563static void
4564forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4565{
4566 TRACE(("In parent shell: child = %d\n", pid));
4567 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004568 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4569 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004570 jobless++;
4571 return;
4572 }
4573#if JOBS
4574 if (mode != FORK_NOJOB && jp->jobctl) {
4575 int pgrp;
4576
4577 if (jp->nprocs == 0)
4578 pgrp = pid;
4579 else
4580 pgrp = jp->ps[0].pid;
4581 /* This can fail because we are doing it in the child also */
4582 setpgid(pid, pgrp);
4583 }
4584#endif
4585 if (mode == FORK_BG) {
4586 backgndpid = pid; /* set $! */
4587 set_curjob(jp, CUR_RUNNING);
4588 }
4589 if (jp) {
4590 struct procstat *ps = &jp->ps[jp->nprocs++];
4591 ps->pid = pid;
4592 ps->status = -1;
4593 ps->cmd = nullstr;
4594#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004595 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004596 ps->cmd = commandtext(n);
4597#endif
4598 }
4599}
4600
4601static int
4602forkshell(struct job *jp, union node *n, int mode)
4603{
4604 int pid;
4605
4606 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4607 pid = fork();
4608 if (pid < 0) {
4609 TRACE(("Fork failed, errno=%d", errno));
4610 if (jp)
4611 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004612 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004613 }
4614 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004615 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004616 else
4617 forkparent(jp, n, mode, pid);
4618 return pid;
4619}
4620
4621/*
4622 * Wait for job to finish.
4623 *
4624 * Under job control we have the problem that while a child process is
4625 * running interrupts generated by the user are sent to the child but not
4626 * to the shell. This means that an infinite loop started by an inter-
4627 * active user may be hard to kill. With job control turned off, an
4628 * interactive user may place an interactive program inside a loop. If
4629 * the interactive program catches interrupts, the user doesn't want
4630 * these interrupts to also abort the loop. The approach we take here
4631 * is to have the shell ignore interrupt signals while waiting for a
4632 * foreground process to terminate, and then send itself an interrupt
4633 * signal if the child process was terminated by an interrupt signal.
4634 * Unfortunately, some programs want to do a bit of cleanup and then
4635 * exit on interrupt; unless these processes terminate themselves by
4636 * sending a signal to themselves (instead of calling exit) they will
4637 * confuse this approach.
4638 *
4639 * Called with interrupts off.
4640 */
4641static int
4642waitforjob(struct job *jp)
4643{
4644 int st;
4645
4646 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4647 while (jp->state == JOBRUNNING) {
4648 dowait(DOWAIT_BLOCK, jp);
4649 }
4650 st = getstatus(jp);
4651#if JOBS
4652 if (jp->jobctl) {
4653 xtcsetpgrp(ttyfd, rootpid);
4654 /*
4655 * This is truly gross.
4656 * If we're doing job control, then we did a TIOCSPGRP which
4657 * caused us (the shell) to no longer be in the controlling
4658 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4659 * intuit from the subprocess exit status whether a SIGINT
4660 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4661 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004662 if (jp->sigint) /* TODO: do the same with all signals */
4663 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004664 }
4665 if (jp->state == JOBDONE)
4666#endif
4667 freejob(jp);
4668 return st;
4669}
4670
4671/*
4672 * return 1 if there are stopped jobs, otherwise 0
4673 */
4674static int
4675stoppedjobs(void)
4676{
4677 struct job *jp;
4678 int retval;
4679
4680 retval = 0;
4681 if (job_warning)
4682 goto out;
4683 jp = curjob;
4684 if (jp && jp->state == JOBSTOPPED) {
4685 out2str("You have stopped jobs.\n");
4686 job_warning = 2;
4687 retval++;
4688 }
4689 out:
4690 return retval;
4691}
4692
4693
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004694/* ============ redir.c
4695 *
4696 * Code for dealing with input/output redirection.
4697 */
4698
4699#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004700#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004701
4702/*
4703 * Open a file in noclobber mode.
4704 * The code was copied from bash.
4705 */
4706static int
4707noclobberopen(const char *fname)
4708{
4709 int r, fd;
4710 struct stat finfo, finfo2;
4711
4712 /*
4713 * If the file exists and is a regular file, return an error
4714 * immediately.
4715 */
4716 r = stat(fname, &finfo);
4717 if (r == 0 && S_ISREG(finfo.st_mode)) {
4718 errno = EEXIST;
4719 return -1;
4720 }
4721
4722 /*
4723 * If the file was not present (r != 0), make sure we open it
4724 * exclusively so that if it is created before we open it, our open
4725 * will fail. Make sure that we do not truncate an existing file.
4726 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4727 * file was not a regular file, we leave O_EXCL off.
4728 */
4729 if (r != 0)
4730 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4731 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4732
4733 /* If the open failed, return the file descriptor right away. */
4734 if (fd < 0)
4735 return fd;
4736
4737 /*
4738 * OK, the open succeeded, but the file may have been changed from a
4739 * non-regular file to a regular file between the stat and the open.
4740 * We are assuming that the O_EXCL open handles the case where FILENAME
4741 * did not exist and is symlinked to an existing file between the stat
4742 * and open.
4743 */
4744
4745 /*
4746 * If we can open it and fstat the file descriptor, and neither check
4747 * revealed that it was a regular file, and the file has not been
4748 * replaced, return the file descriptor.
4749 */
4750 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4751 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4752 return fd;
4753
4754 /* The file has been replaced. badness. */
4755 close(fd);
4756 errno = EEXIST;
4757 return -1;
4758}
4759
4760/*
4761 * Handle here documents. Normally we fork off a process to write the
4762 * data to a pipe. If the document is short, we can stuff the data in
4763 * the pipe without forking.
4764 */
4765/* openhere needs this forward reference */
4766static void expandhere(union node *arg, int fd);
4767static int
4768openhere(union node *redir)
4769{
4770 int pip[2];
4771 size_t len = 0;
4772
4773 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004774 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004775 if (redir->type == NHERE) {
4776 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004777 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004778 full_write(pip[1], redir->nhere.doc->narg.text, len);
4779 goto out;
4780 }
4781 }
4782 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004783 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004784 close(pip[0]);
4785 signal(SIGINT, SIG_IGN);
4786 signal(SIGQUIT, SIG_IGN);
4787 signal(SIGHUP, SIG_IGN);
4788#ifdef SIGTSTP
4789 signal(SIGTSTP, SIG_IGN);
4790#endif
4791 signal(SIGPIPE, SIG_DFL);
4792 if (redir->type == NHERE)
4793 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004794 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004795 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004796 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004797 }
4798 out:
4799 close(pip[1]);
4800 return pip[0];
4801}
4802
4803static int
4804openredirect(union node *redir)
4805{
4806 char *fname;
4807 int f;
4808
4809 switch (redir->nfile.type) {
4810 case NFROM:
4811 fname = redir->nfile.expfname;
4812 f = open(fname, O_RDONLY);
4813 if (f < 0)
4814 goto eopen;
4815 break;
4816 case NFROMTO:
4817 fname = redir->nfile.expfname;
4818 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4819 if (f < 0)
4820 goto ecreate;
4821 break;
4822 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004823#if ENABLE_ASH_BASH_COMPAT
4824 case NTO2:
4825#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004826 /* Take care of noclobber mode. */
4827 if (Cflag) {
4828 fname = redir->nfile.expfname;
4829 f = noclobberopen(fname);
4830 if (f < 0)
4831 goto ecreate;
4832 break;
4833 }
4834 /* FALLTHROUGH */
4835 case NCLOBBER:
4836 fname = redir->nfile.expfname;
4837 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4838 if (f < 0)
4839 goto ecreate;
4840 break;
4841 case NAPPEND:
4842 fname = redir->nfile.expfname;
4843 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4844 if (f < 0)
4845 goto ecreate;
4846 break;
4847 default:
4848#if DEBUG
4849 abort();
4850#endif
4851 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004852/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004853// case NTOFD:
4854// case NFROMFD:
4855// f = -1;
4856// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004857 case NHERE:
4858 case NXHERE:
4859 f = openhere(redir);
4860 break;
4861 }
4862
4863 return f;
4864 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004865 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004866 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004867 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004868}
4869
4870/*
4871 * Copy a file descriptor to be >= to. Returns -1
4872 * if the source file descriptor is closed, EMPTY if there are no unused
4873 * file descriptors left.
4874 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004875/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4876 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004877enum {
4878 COPYFD_EXACT = (int)~(INT_MAX),
4879 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4880};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004881static int
4882copyfd(int from, int to)
4883{
4884 int newfd;
4885
Denis Vlasenko5a867312008-07-24 19:46:38 +00004886 if (to & COPYFD_EXACT) {
4887 to &= ~COPYFD_EXACT;
4888 /*if (from != to)*/
4889 newfd = dup2(from, to);
4890 } else {
4891 newfd = fcntl(from, F_DUPFD, to);
4892 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004893 if (newfd < 0) {
4894 if (errno == EMFILE)
4895 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004896 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004897 ash_msg_and_raise_error("%d: %m", from);
4898 }
4899 return newfd;
4900}
4901
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004902/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004903struct two_fd_t {
4904 int orig, copy;
4905};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004906struct redirtab {
4907 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004908 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004909 int pair_count;
4910 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004911};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004912#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004913
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004914static int need_to_remember(struct redirtab *rp, int fd)
4915{
4916 int i;
4917
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004918 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004919 return 0;
4920
4921 for (i = 0; i < rp->pair_count; i++) {
4922 if (rp->two_fd[i].orig == fd) {
4923 /* already remembered */
4924 return 0;
4925 }
4926 }
4927 return 1;
4928}
4929
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004930/* "hidden" fd is a fd used to read scripts, or a copy of such */
4931static int is_hidden_fd(struct redirtab *rp, int fd)
4932{
4933 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00004934 struct parsefile *pf;
4935
4936 if (fd == -1)
4937 return 0;
4938 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004939 while (pf) {
4940 if (fd == pf->fd) {
4941 return 1;
4942 }
4943 pf = pf->prev;
4944 }
4945 if (!rp)
4946 return 0;
4947 fd |= COPYFD_RESTORE;
4948 for (i = 0; i < rp->pair_count; i++) {
4949 if (rp->two_fd[i].copy == fd) {
4950 return 1;
4951 }
4952 }
4953 return 0;
4954}
4955
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004956/*
4957 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4958 * old file descriptors are stashed away so that the redirection can be
4959 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4960 * standard output, and the standard error if it becomes a duplicate of
4961 * stdout, is saved in memory.
4962 */
4963/* flags passed to redirect */
4964#define REDIR_PUSH 01 /* save previous values of file descriptors */
4965#define REDIR_SAVEFD2 03 /* set preverrout */
4966static void
4967redirect(union node *redir, int flags)
4968{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004969 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004970 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004971 int i;
4972 int fd;
4973 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004974 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004975
Denis Vlasenko01631112007-12-16 17:20:38 +00004976 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004977 if (!redir) {
4978 return;
4979 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004980
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004981 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004982 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004983 INT_OFF;
4984 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004985 union node *tmp = redir;
4986 do {
4987 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004988#if ENABLE_ASH_BASH_COMPAT
4989 if (redir->nfile.type == NTO2)
4990 sv_pos++;
4991#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004992 tmp = tmp->nfile.next;
4993 } while (tmp);
4994 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004995 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004996 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004997 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004998 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004999 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005000 while (sv_pos > 0) {
5001 sv_pos--;
5002 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5003 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005004 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005005
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005006 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005007 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005008 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005009 int right_fd = redir->ndup.dupfd;
5010 /* redirect from/to same file descriptor? */
5011 if (right_fd == fd)
5012 continue;
5013 /* echo >&10 and 10 is a fd opened to the sh script? */
5014 if (is_hidden_fd(sv, right_fd)) {
5015 errno = EBADF; /* as if it is closed */
5016 ash_msg_and_raise_error("%d: %m", right_fd);
5017 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005018 newfd = -1;
5019 } else {
5020 newfd = openredirect(redir); /* always >= 0 */
5021 if (fd == newfd) {
5022 /* Descriptor wasn't open before redirect.
5023 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005024 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005025 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005026 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005027 continue;
5028 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005029 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005030#if ENABLE_ASH_BASH_COMPAT
5031 redirect_more:
5032#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005033 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005034 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005035 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005036/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5037 * are closed in popredir() in the child, preventing them from leaking
5038 * into child. (popredir() also cleans up the mess in case of failures)
5039 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005040 if (i == -1) {
5041 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005042 if (i != EBADF) {
5043 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005044 if (newfd >= 0)
5045 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005046 errno = i;
5047 ash_msg_and_raise_error("%d: %m", fd);
5048 /* NOTREACHED */
5049 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005050 /* EBADF: it is not open - good, remember to close it */
5051 remember_to_close:
5052 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005053 } else { /* fd is open, save its copy */
5054 /* "exec fd>&-" should not close fds
5055 * which point to script file(s).
5056 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005057 if (is_hidden_fd(sv, fd))
5058 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005059 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005060 if (fd == 2)
5061 copied_fd2 = i;
5062 sv->two_fd[sv_pos].orig = fd;
5063 sv->two_fd[sv_pos].copy = i;
5064 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005065 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005066 if (newfd < 0) {
5067 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005068 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005069 close(fd);
5070 } else {
5071 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005072 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005073 } else if (fd != newfd) { /* move newfd to fd */
5074 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005075#if ENABLE_ASH_BASH_COMPAT
5076 if (!(redir->nfile.type == NTO2 && fd == 2))
5077#endif
5078 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005079 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005080#if ENABLE_ASH_BASH_COMPAT
5081 if (redir->nfile.type == NTO2 && fd == 1) {
5082 /* We already redirected it to fd 1, now copy it to 2 */
5083 newfd = 1;
5084 fd = 2;
5085 goto redirect_more;
5086 }
5087#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005088 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005089
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005090 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005091 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5092 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005093}
5094
5095/*
5096 * Undo the effects of the last redirection.
5097 */
5098static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005099popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005100{
5101 struct redirtab *rp;
5102 int i;
5103
Denis Vlasenko01631112007-12-16 17:20:38 +00005104 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005105 return;
5106 INT_OFF;
5107 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005108 for (i = 0; i < rp->pair_count; i++) {
5109 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005110 int copy = rp->two_fd[i].copy;
5111 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005112 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005113 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005114 continue;
5115 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005116 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005117 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005118 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005119 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005120 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005121 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005122 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005123 }
5124 }
5125 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005126 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005127 free(rp);
5128 INT_ON;
5129}
5130
5131/*
5132 * Undo all redirections. Called on error or interrupt.
5133 */
5134
5135/*
5136 * Discard all saved file descriptors.
5137 */
5138static void
5139clearredir(int drop)
5140{
5141 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005142 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005143 if (!redirlist)
5144 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005145 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005146 }
5147}
5148
5149static int
5150redirectsafe(union node *redir, int flags)
5151{
5152 int err;
5153 volatile int saveint;
5154 struct jmploc *volatile savehandler = exception_handler;
5155 struct jmploc jmploc;
5156
5157 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005158 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5159 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160 if (!err) {
5161 exception_handler = &jmploc;
5162 redirect(redir, flags);
5163 }
5164 exception_handler = savehandler;
5165 if (err && exception != EXERROR)
5166 longjmp(exception_handler->loc, 1);
5167 RESTORE_INT(saveint);
5168 return err;
5169}
5170
5171
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005172/* ============ Routines to expand arguments to commands
5173 *
5174 * We have to deal with backquotes, shell variables, and file metacharacters.
5175 */
5176
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005177#if ENABLE_ASH_MATH_SUPPORT_64
5178typedef int64_t arith_t;
5179#define arith_t_type long long
5180#else
5181typedef long arith_t;
5182#define arith_t_type long
5183#endif
5184
5185#if ENABLE_ASH_MATH_SUPPORT
5186static arith_t dash_arith(const char *);
5187static arith_t arith(const char *expr, int *perrcode);
5188#endif
5189
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005190/*
5191 * expandarg flags
5192 */
5193#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5194#define EXP_TILDE 0x2 /* do normal tilde expansion */
5195#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5196#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5197#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5198#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5199#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5200#define EXP_WORD 0x80 /* expand word in parameter expansion */
5201#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5202/*
5203 * _rmescape() flags
5204 */
5205#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5206#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5207#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5208#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5209#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5210
5211/*
5212 * Structure specifying which parts of the string should be searched
5213 * for IFS characters.
5214 */
5215struct ifsregion {
5216 struct ifsregion *next; /* next region in list */
5217 int begoff; /* offset of start of region */
5218 int endoff; /* offset of end of region */
5219 int nulonly; /* search for nul bytes only */
5220};
5221
5222struct arglist {
5223 struct strlist *list;
5224 struct strlist **lastp;
5225};
5226
5227/* output of current string */
5228static char *expdest;
5229/* list of back quote expressions */
5230static struct nodelist *argbackq;
5231/* first struct in list of ifs regions */
5232static struct ifsregion ifsfirst;
5233/* last struct in list */
5234static struct ifsregion *ifslastp;
5235/* holds expanded arg list */
5236static struct arglist exparg;
5237
5238/*
5239 * Our own itoa().
5240 */
5241static int
5242cvtnum(arith_t num)
5243{
5244 int len;
5245
5246 expdest = makestrspace(32, expdest);
5247#if ENABLE_ASH_MATH_SUPPORT_64
5248 len = fmtstr(expdest, 32, "%lld", (long long) num);
5249#else
5250 len = fmtstr(expdest, 32, "%ld", num);
5251#endif
5252 STADJUST(len, expdest);
5253 return len;
5254}
5255
5256static size_t
5257esclen(const char *start, const char *p)
5258{
5259 size_t esc = 0;
5260
5261 while (p > start && *--p == CTLESC) {
5262 esc++;
5263 }
5264 return esc;
5265}
5266
5267/*
5268 * Remove any CTLESC characters from a string.
5269 */
5270static char *
5271_rmescapes(char *str, int flag)
5272{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005273 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005274
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005275 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005276 unsigned inquotes;
5277 int notescaped;
5278 int globbing;
5279
5280 p = strpbrk(str, qchars);
5281 if (!p) {
5282 return str;
5283 }
5284 q = p;
5285 r = str;
5286 if (flag & RMESCAPE_ALLOC) {
5287 size_t len = p - str;
5288 size_t fulllen = len + strlen(p) + 1;
5289
5290 if (flag & RMESCAPE_GROW) {
5291 r = makestrspace(fulllen, expdest);
5292 } else if (flag & RMESCAPE_HEAP) {
5293 r = ckmalloc(fulllen);
5294 } else {
5295 r = stalloc(fulllen);
5296 }
5297 q = r;
5298 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005299 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005300 }
5301 }
5302 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5303 globbing = flag & RMESCAPE_GLOB;
5304 notescaped = globbing;
5305 while (*p) {
5306 if (*p == CTLQUOTEMARK) {
5307 inquotes = ~inquotes;
5308 p++;
5309 notescaped = globbing;
5310 continue;
5311 }
5312 if (*p == '\\') {
5313 /* naked back slash */
5314 notescaped = 0;
5315 goto copy;
5316 }
5317 if (*p == CTLESC) {
5318 p++;
5319 if (notescaped && inquotes && *p != '/') {
5320 *q++ = '\\';
5321 }
5322 }
5323 notescaped = globbing;
5324 copy:
5325 *q++ = *p++;
5326 }
5327 *q = '\0';
5328 if (flag & RMESCAPE_GROW) {
5329 expdest = r;
5330 STADJUST(q - r + 1, expdest);
5331 }
5332 return r;
5333}
5334#define rmescapes(p) _rmescapes((p), 0)
5335
5336#define pmatch(a, b) !fnmatch((a), (b), 0)
5337
5338/*
5339 * Prepare a pattern for a expmeta (internal glob(3)) call.
5340 *
5341 * Returns an stalloced string.
5342 */
5343static char *
5344preglob(const char *pattern, int quoted, int flag)
5345{
5346 flag |= RMESCAPE_GLOB;
5347 if (quoted) {
5348 flag |= RMESCAPE_QUOTED;
5349 }
5350 return _rmescapes((char *)pattern, flag);
5351}
5352
5353/*
5354 * Put a string on the stack.
5355 */
5356static void
5357memtodest(const char *p, size_t len, int syntax, int quotes)
5358{
5359 char *q = expdest;
5360
5361 q = makestrspace(len * 2, q);
5362
5363 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005364 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005365 if (!c)
5366 continue;
5367 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5368 USTPUTC(CTLESC, q);
5369 USTPUTC(c, q);
5370 }
5371
5372 expdest = q;
5373}
5374
5375static void
5376strtodest(const char *p, int syntax, int quotes)
5377{
5378 memtodest(p, strlen(p), syntax, quotes);
5379}
5380
5381/*
5382 * Record the fact that we have to scan this region of the
5383 * string for IFS characters.
5384 */
5385static void
5386recordregion(int start, int end, int nulonly)
5387{
5388 struct ifsregion *ifsp;
5389
5390 if (ifslastp == NULL) {
5391 ifsp = &ifsfirst;
5392 } else {
5393 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005394 ifsp = ckzalloc(sizeof(*ifsp));
5395 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005396 ifslastp->next = ifsp;
5397 INT_ON;
5398 }
5399 ifslastp = ifsp;
5400 ifslastp->begoff = start;
5401 ifslastp->endoff = end;
5402 ifslastp->nulonly = nulonly;
5403}
5404
5405static void
5406removerecordregions(int endoff)
5407{
5408 if (ifslastp == NULL)
5409 return;
5410
5411 if (ifsfirst.endoff > endoff) {
5412 while (ifsfirst.next != NULL) {
5413 struct ifsregion *ifsp;
5414 INT_OFF;
5415 ifsp = ifsfirst.next->next;
5416 free(ifsfirst.next);
5417 ifsfirst.next = ifsp;
5418 INT_ON;
5419 }
5420 if (ifsfirst.begoff > endoff)
5421 ifslastp = NULL;
5422 else {
5423 ifslastp = &ifsfirst;
5424 ifsfirst.endoff = endoff;
5425 }
5426 return;
5427 }
5428
5429 ifslastp = &ifsfirst;
5430 while (ifslastp->next && ifslastp->next->begoff < endoff)
5431 ifslastp=ifslastp->next;
5432 while (ifslastp->next != NULL) {
5433 struct ifsregion *ifsp;
5434 INT_OFF;
5435 ifsp = ifslastp->next->next;
5436 free(ifslastp->next);
5437 ifslastp->next = ifsp;
5438 INT_ON;
5439 }
5440 if (ifslastp->endoff > endoff)
5441 ifslastp->endoff = endoff;
5442}
5443
5444static char *
5445exptilde(char *startp, char *p, int flag)
5446{
5447 char c;
5448 char *name;
5449 struct passwd *pw;
5450 const char *home;
5451 int quotes = flag & (EXP_FULL | EXP_CASE);
5452 int startloc;
5453
5454 name = p + 1;
5455
5456 while ((c = *++p) != '\0') {
5457 switch (c) {
5458 case CTLESC:
5459 return startp;
5460 case CTLQUOTEMARK:
5461 return startp;
5462 case ':':
5463 if (flag & EXP_VARTILDE)
5464 goto done;
5465 break;
5466 case '/':
5467 case CTLENDVAR:
5468 goto done;
5469 }
5470 }
5471 done:
5472 *p = '\0';
5473 if (*name == '\0') {
5474 home = lookupvar(homestr);
5475 } else {
5476 pw = getpwnam(name);
5477 if (pw == NULL)
5478 goto lose;
5479 home = pw->pw_dir;
5480 }
5481 if (!home || !*home)
5482 goto lose;
5483 *p = c;
5484 startloc = expdest - (char *)stackblock();
5485 strtodest(home, SQSYNTAX, quotes);
5486 recordregion(startloc, expdest - (char *)stackblock(), 0);
5487 return p;
5488 lose:
5489 *p = c;
5490 return startp;
5491}
5492
5493/*
5494 * Execute a command inside back quotes. If it's a builtin command, we
5495 * want to save its output in a block obtained from malloc. Otherwise
5496 * we fork off a subprocess and get the output of the command via a pipe.
5497 * Should be called with interrupts off.
5498 */
5499struct backcmd { /* result of evalbackcmd */
5500 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005501 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005502 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005503 struct job *jp; /* job structure for command */
5504};
5505
5506/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005507static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005508#define EV_EXIT 01 /* exit after evaluating tree */
5509static void evaltree(union node *, int);
5510
5511static void
5512evalbackcmd(union node *n, struct backcmd *result)
5513{
5514 int saveherefd;
5515
5516 result->fd = -1;
5517 result->buf = NULL;
5518 result->nleft = 0;
5519 result->jp = NULL;
5520 if (n == NULL) {
5521 goto out;
5522 }
5523
5524 saveherefd = herefd;
5525 herefd = -1;
5526
5527 {
5528 int pip[2];
5529 struct job *jp;
5530
5531 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005532 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005533 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005534 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5535 FORCE_INT_ON;
5536 close(pip[0]);
5537 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005538 /*close(1);*/
5539 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005540 close(pip[1]);
5541 }
5542 eflag = 0;
5543 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5544 /* NOTREACHED */
5545 }
5546 close(pip[1]);
5547 result->fd = pip[0];
5548 result->jp = jp;
5549 }
5550 herefd = saveherefd;
5551 out:
5552 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5553 result->fd, result->buf, result->nleft, result->jp));
5554}
5555
5556/*
5557 * Expand stuff in backwards quotes.
5558 */
5559static void
5560expbackq(union node *cmd, int quoted, int quotes)
5561{
5562 struct backcmd in;
5563 int i;
5564 char buf[128];
5565 char *p;
5566 char *dest;
5567 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005568 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569 struct stackmark smark;
5570
5571 INT_OFF;
5572 setstackmark(&smark);
5573 dest = expdest;
5574 startloc = dest - (char *)stackblock();
5575 grabstackstr(dest);
5576 evalbackcmd(cmd, &in);
5577 popstackmark(&smark);
5578
5579 p = in.buf;
5580 i = in.nleft;
5581 if (i == 0)
5582 goto read;
5583 for (;;) {
5584 memtodest(p, i, syntax, quotes);
5585 read:
5586 if (in.fd < 0)
5587 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005588 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005589 TRACE(("expbackq: read returns %d\n", i));
5590 if (i <= 0)
5591 break;
5592 p = buf;
5593 }
5594
Denis Vlasenko60818682007-09-28 22:07:23 +00005595 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596 if (in.fd >= 0) {
5597 close(in.fd);
5598 back_exitstatus = waitforjob(in.jp);
5599 }
5600 INT_ON;
5601
5602 /* Eat all trailing newlines */
5603 dest = expdest;
5604 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5605 STUNPUTC(dest);
5606 expdest = dest;
5607
5608 if (quoted == 0)
5609 recordregion(startloc, dest - (char *)stackblock(), 0);
5610 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5611 (dest - (char *)stackblock()) - startloc,
5612 (dest - (char *)stackblock()) - startloc,
5613 stackblock() + startloc));
5614}
5615
5616#if ENABLE_ASH_MATH_SUPPORT
5617/*
5618 * Expand arithmetic expression. Backup to start of expression,
5619 * evaluate, place result in (backed up) result, adjust string position.
5620 */
5621static void
5622expari(int quotes)
5623{
5624 char *p, *start;
5625 int begoff;
5626 int flag;
5627 int len;
5628
5629 /* ifsfree(); */
5630
5631 /*
5632 * This routine is slightly over-complicated for
5633 * efficiency. Next we scan backwards looking for the
5634 * start of arithmetic.
5635 */
5636 start = stackblock();
5637 p = expdest - 1;
5638 *p = '\0';
5639 p--;
5640 do {
5641 int esc;
5642
5643 while (*p != CTLARI) {
5644 p--;
5645#if DEBUG
5646 if (p < start) {
5647 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5648 }
5649#endif
5650 }
5651
5652 esc = esclen(start, p);
5653 if (!(esc % 2)) {
5654 break;
5655 }
5656
5657 p -= esc + 1;
5658 } while (1);
5659
5660 begoff = p - start;
5661
5662 removerecordregions(begoff);
5663
5664 flag = p[1];
5665
5666 expdest = p;
5667
5668 if (quotes)
5669 rmescapes(p + 2);
5670
5671 len = cvtnum(dash_arith(p + 2));
5672
5673 if (flag != '"')
5674 recordregion(begoff, begoff + len, 0);
5675}
5676#endif
5677
5678/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005679static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005680
5681/*
5682 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5683 * characters to allow for further processing. Otherwise treat
5684 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005685 *
5686 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5687 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5688 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005689 */
5690static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005691argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005692{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005693 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005694 '=',
5695 ':',
5696 CTLQUOTEMARK,
5697 CTLENDVAR,
5698 CTLESC,
5699 CTLVAR,
5700 CTLBACKQ,
5701 CTLBACKQ | CTLQUOTE,
5702#if ENABLE_ASH_MATH_SUPPORT
5703 CTLENDARI,
5704#endif
5705 0
5706 };
5707 const char *reject = spclchars;
5708 int c;
5709 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5710 int breakall = flag & EXP_WORD;
5711 int inquotes;
5712 size_t length;
5713 int startloc;
5714
5715 if (!(flag & EXP_VARTILDE)) {
5716 reject += 2;
5717 } else if (flag & EXP_VARTILDE2) {
5718 reject++;
5719 }
5720 inquotes = 0;
5721 length = 0;
5722 if (flag & EXP_TILDE) {
5723 char *q;
5724
5725 flag &= ~EXP_TILDE;
5726 tilde:
5727 q = p;
5728 if (*q == CTLESC && (flag & EXP_QWORD))
5729 q++;
5730 if (*q == '~')
5731 p = exptilde(p, q, flag);
5732 }
5733 start:
5734 startloc = expdest - (char *)stackblock();
5735 for (;;) {
5736 length += strcspn(p + length, reject);
5737 c = p[length];
5738 if (c && (!(c & 0x80)
5739#if ENABLE_ASH_MATH_SUPPORT
5740 || c == CTLENDARI
5741#endif
5742 )) {
5743 /* c == '=' || c == ':' || c == CTLENDARI */
5744 length++;
5745 }
5746 if (length > 0) {
5747 int newloc;
5748 expdest = stack_nputstr(p, length, expdest);
5749 newloc = expdest - (char *)stackblock();
5750 if (breakall && !inquotes && newloc > startloc) {
5751 recordregion(startloc, newloc, 0);
5752 }
5753 startloc = newloc;
5754 }
5755 p += length + 1;
5756 length = 0;
5757
5758 switch (c) {
5759 case '\0':
5760 goto breakloop;
5761 case '=':
5762 if (flag & EXP_VARTILDE2) {
5763 p--;
5764 continue;
5765 }
5766 flag |= EXP_VARTILDE2;
5767 reject++;
5768 /* fall through */
5769 case ':':
5770 /*
5771 * sort of a hack - expand tildes in variable
5772 * assignments (after the first '=' and after ':'s).
5773 */
5774 if (*--p == '~') {
5775 goto tilde;
5776 }
5777 continue;
5778 }
5779
5780 switch (c) {
5781 case CTLENDVAR: /* ??? */
5782 goto breakloop;
5783 case CTLQUOTEMARK:
5784 /* "$@" syntax adherence hack */
5785 if (
5786 !inquotes &&
5787 !memcmp(p, dolatstr, 4) &&
5788 (p[4] == CTLQUOTEMARK || (
5789 p[4] == CTLENDVAR &&
5790 p[5] == CTLQUOTEMARK
5791 ))
5792 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005793 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005794 goto start;
5795 }
5796 inquotes = !inquotes;
5797 addquote:
5798 if (quotes) {
5799 p--;
5800 length++;
5801 startloc++;
5802 }
5803 break;
5804 case CTLESC:
5805 startloc++;
5806 length++;
5807 goto addquote;
5808 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005809 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005810 goto start;
5811 case CTLBACKQ:
5812 c = 0;
5813 case CTLBACKQ|CTLQUOTE:
5814 expbackq(argbackq->n, c, quotes);
5815 argbackq = argbackq->next;
5816 goto start;
5817#if ENABLE_ASH_MATH_SUPPORT
5818 case CTLENDARI:
5819 p--;
5820 expari(quotes);
5821 goto start;
5822#endif
5823 }
5824 }
5825 breakloop:
5826 ;
5827}
5828
5829static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005830scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005831 int zero)
5832{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005833// This commented out code was added by James Simmons <jsimmons@infradead.org>
5834// as part of a larger change when he added support for ${var/a/b}.
5835// However, it broke # and % operators:
5836//
5837//var=ababcdcd
5838// ok bad
5839//echo ${var#ab} abcdcd abcdcd
5840//echo ${var##ab} abcdcd abcdcd
5841//echo ${var#a*b} abcdcd ababcdcd (!)
5842//echo ${var##a*b} cdcd cdcd
5843//echo ${var#?} babcdcd ababcdcd (!)
5844//echo ${var##?} babcdcd babcdcd
5845//echo ${var#*} ababcdcd babcdcd (!)
5846//echo ${var##*}
5847//echo ${var%cd} ababcd ababcd
5848//echo ${var%%cd} ababcd abab (!)
5849//echo ${var%c*d} ababcd ababcd
5850//echo ${var%%c*d} abab ababcdcd (!)
5851//echo ${var%?} ababcdc ababcdc
5852//echo ${var%%?} ababcdc ababcdcd (!)
5853//echo ${var%*} ababcdcd ababcdcd
5854//echo ${var%%*}
5855//
5856// Commenting it back out helped. Remove it completely if it really
5857// is not needed.
5858
5859 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860 char c;
5861
5862 loc = startp;
5863 loc2 = rmesc;
5864 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005865 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005866 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005867
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 c = *loc2;
5869 if (zero) {
5870 *loc2 = '\0';
5871 s = rmesc;
5872 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005873 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005874
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005875// // chop off end if its '*'
5876// full = strrchr(str, '*');
5877// if (full && full != str)
5878// match--;
5879//
5880// // If str starts with '*' replace with s.
5881// if ((*str == '*') && strlen(s) >= match) {
5882// full = xstrdup(s);
5883// strncpy(full+strlen(s)-match+1, str+1, match-1);
5884// } else
5885// full = xstrndup(str, match);
5886// match = strncmp(s, full, strlen(full));
5887// free(full);
5888//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005889 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005890 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005891 return loc;
5892 if (quotes && *loc == CTLESC)
5893 loc++;
5894 loc++;
5895 loc2++;
5896 } while (c);
5897 return 0;
5898}
5899
5900static char *
5901scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5902 int zero)
5903{
5904 int esc = 0;
5905 char *loc;
5906 char *loc2;
5907
5908 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5909 int match;
5910 char c = *loc2;
5911 const char *s = loc2;
5912 if (zero) {
5913 *loc2 = '\0';
5914 s = rmesc;
5915 }
5916 match = pmatch(str, s);
5917 *loc2 = c;
5918 if (match)
5919 return loc;
5920 loc--;
5921 if (quotes) {
5922 if (--esc < 0) {
5923 esc = esclen(startp, loc);
5924 }
5925 if (esc % 2) {
5926 esc--;
5927 loc--;
5928 }
5929 }
5930 }
5931 return 0;
5932}
5933
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005934static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005935static void
5936varunset(const char *end, const char *var, const char *umsg, int varflags)
5937{
5938 const char *msg;
5939 const char *tail;
5940
5941 tail = nullstr;
5942 msg = "parameter not set";
5943 if (umsg) {
5944 if (*end == CTLENDVAR) {
5945 if (varflags & VSNUL)
5946 tail = " or null";
5947 } else
5948 msg = umsg;
5949 }
5950 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5951}
5952
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953#if ENABLE_ASH_BASH_COMPAT
5954static char *
5955parse_sub_pattern(char *arg, int inquotes)
5956{
5957 char *idx, *repl = NULL;
5958 unsigned char c;
5959
Denis Vlasenko2659c632008-06-14 06:04:59 +00005960 idx = arg;
5961 while (1) {
5962 c = *arg;
5963 if (!c)
5964 break;
5965 if (c == '/') {
5966 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005967 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00005968 repl = idx + 1;
5969 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005970 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005971 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00005972 *idx++ = c;
5973 if (!inquotes && c == '\\' && arg[1] == '\\')
5974 arg++; /* skip both \\, not just first one */
5975 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005976 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00005977 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005978
5979 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005980}
5981#endif /* ENABLE_ASH_BASH_COMPAT */
5982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005983static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005984subevalvar(char *p, char *str, int strloc, int subtype,
5985 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005986{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005987 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005988 char *startp;
5989 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005990 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005991 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5992 USE_ASH_BASH_COMPAT(char null = '\0';)
5993 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5994 int saveherefd = herefd;
5995 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005997 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005998
5999 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006000 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6001 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006002 STPUTC('\0', expdest);
6003 herefd = saveherefd;
6004 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006005 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006006
6007 switch (subtype) {
6008 case VSASSIGN:
6009 setvar(str, startp, 0);
6010 amount = startp - expdest;
6011 STADJUST(amount, expdest);
6012 return startp;
6013
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006014#if ENABLE_ASH_BASH_COMPAT
6015 case VSSUBSTR:
6016 loc = str = stackblock() + strloc;
6017// TODO: number() instead? It does error checking...
6018 pos = atoi(loc);
6019 len = str - startp - 1;
6020
6021 /* *loc != '\0', guaranteed by parser */
6022 if (quotes) {
6023 char *ptr;
6024
6025 /* We must adjust the length by the number of escapes we find. */
6026 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006027 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006028 len--;
6029 ptr++;
6030 }
6031 }
6032 }
6033 orig_len = len;
6034
6035 if (*loc++ == ':') {
6036// TODO: number() instead? It does error checking...
6037 len = atoi(loc);
6038 } else {
6039 len = orig_len;
6040 while (*loc && *loc != ':')
6041 loc++;
6042 if (*loc++ == ':')
6043// TODO: number() instead? It does error checking...
6044 len = atoi(loc);
6045 }
6046 if (pos >= orig_len) {
6047 pos = 0;
6048 len = 0;
6049 }
6050 if (len > (orig_len - pos))
6051 len = orig_len - pos;
6052
6053 for (str = startp; pos; str++, pos--) {
6054 if (quotes && *str == CTLESC)
6055 str++;
6056 }
6057 for (loc = startp; len; len--) {
6058 if (quotes && *str == CTLESC)
6059 *loc++ = *str++;
6060 *loc++ = *str++;
6061 }
6062 *loc = '\0';
6063 amount = loc - expdest;
6064 STADJUST(amount, expdest);
6065 return loc;
6066#endif
6067
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006068 case VSQUESTION:
6069 varunset(p, str, startp, varflags);
6070 /* NOTREACHED */
6071 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006072 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006073
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006074 /* We'll comeback here if we grow the stack while handling
6075 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6076 * stack will need rebasing, and we'll need to remove our work
6077 * areas each time
6078 */
6079 USE_ASH_BASH_COMPAT(restart:)
6080
6081 amount = expdest - ((char *)stackblock() + resetloc);
6082 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006083 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006084
6085 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006086 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006087 if (quotes) {
6088 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6089 if (rmesc != startp) {
6090 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006091 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006092 }
6093 }
6094 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006095 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006096 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006097 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006098
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006099#if ENABLE_ASH_BASH_COMPAT
6100 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6101 char *idx, *end, *restart_detect;
6102
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006103 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006104 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6105 if (!repl)
6106 repl = &null;
6107 }
6108
6109 /* If there's no pattern to match, return the expansion unmolested */
6110 if (*str == '\0')
6111 return 0;
6112
6113 len = 0;
6114 idx = startp;
6115 end = str - 1;
6116 while (idx < end) {
6117 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6118 if (!loc) {
6119 /* No match, advance */
6120 restart_detect = stackblock();
6121 STPUTC(*idx, expdest);
6122 if (quotes && *idx == CTLESC) {
6123 idx++;
6124 len++;
6125 STPUTC(*idx, expdest);
6126 }
6127 if (stackblock() != restart_detect)
6128 goto restart;
6129 idx++;
6130 len++;
6131 rmesc++;
6132 continue;
6133 }
6134
6135 if (subtype == VSREPLACEALL) {
6136 while (idx < loc) {
6137 if (quotes && *idx == CTLESC)
6138 idx++;
6139 idx++;
6140 rmesc++;
6141 }
6142 } else
6143 idx = loc;
6144
6145 for (loc = repl; *loc; loc++) {
6146 restart_detect = stackblock();
6147 STPUTC(*loc, expdest);
6148 if (stackblock() != restart_detect)
6149 goto restart;
6150 len++;
6151 }
6152
6153 if (subtype == VSREPLACE) {
6154 while (*idx) {
6155 restart_detect = stackblock();
6156 STPUTC(*idx, expdest);
6157 if (stackblock() != restart_detect)
6158 goto restart;
6159 len++;
6160 idx++;
6161 }
6162 break;
6163 }
6164 }
6165
6166 /* We've put the replaced text into a buffer at workloc, now
6167 * move it to the right place and adjust the stack.
6168 */
6169 startp = stackblock() + startloc;
6170 STPUTC('\0', expdest);
6171 memmove(startp, stackblock() + workloc, len);
6172 startp[len++] = '\0';
6173 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6174 STADJUST(-amount, expdest);
6175 return startp;
6176 }
6177#endif /* ENABLE_ASH_BASH_COMPAT */
6178
6179 subtype -= VSTRIMRIGHT;
6180#if DEBUG
6181 if (subtype < 0 || subtype > 7)
6182 abort();
6183#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006184 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6185 zero = subtype >> 1;
6186 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6187 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6188
6189 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6190 if (loc) {
6191 if (zero) {
6192 memmove(startp, loc, str - loc);
6193 loc = startp + (str - loc) - 1;
6194 }
6195 *loc = '\0';
6196 amount = loc - expdest;
6197 STADJUST(amount, expdest);
6198 }
6199 return loc;
6200}
6201
6202/*
6203 * Add the value of a specialized variable to the stack string.
6204 */
6205static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006206varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006207{
6208 int num;
6209 char *p;
6210 int i;
6211 int sep = 0;
6212 int sepq = 0;
6213 ssize_t len = 0;
6214 char **ap;
6215 int syntax;
6216 int quoted = varflags & VSQUOTE;
6217 int subtype = varflags & VSTYPE;
6218 int quotes = flags & (EXP_FULL | EXP_CASE);
6219
6220 if (quoted && (flags & EXP_FULL))
6221 sep = 1 << CHAR_BIT;
6222
6223 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6224 switch (*name) {
6225 case '$':
6226 num = rootpid;
6227 goto numvar;
6228 case '?':
6229 num = exitstatus;
6230 goto numvar;
6231 case '#':
6232 num = shellparam.nparam;
6233 goto numvar;
6234 case '!':
6235 num = backgndpid;
6236 if (num == 0)
6237 return -1;
6238 numvar:
6239 len = cvtnum(num);
6240 break;
6241 case '-':
6242 p = makestrspace(NOPTS, expdest);
6243 for (i = NOPTS - 1; i >= 0; i--) {
6244 if (optlist[i]) {
6245 USTPUTC(optletters(i), p);
6246 len++;
6247 }
6248 }
6249 expdest = p;
6250 break;
6251 case '@':
6252 if (sep)
6253 goto param;
6254 /* fall through */
6255 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006256 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006257 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6258 sepq = 1;
6259 param:
6260 ap = shellparam.p;
6261 if (!ap)
6262 return -1;
6263 while ((p = *ap++)) {
6264 size_t partlen;
6265
6266 partlen = strlen(p);
6267 len += partlen;
6268
6269 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6270 memtodest(p, partlen, syntax, quotes);
6271
6272 if (*ap && sep) {
6273 char *q;
6274
6275 len++;
6276 if (subtype == VSPLUS || subtype == VSLENGTH) {
6277 continue;
6278 }
6279 q = expdest;
6280 if (sepq)
6281 STPUTC(CTLESC, q);
6282 STPUTC(sep, q);
6283 expdest = q;
6284 }
6285 }
6286 return len;
6287 case '0':
6288 case '1':
6289 case '2':
6290 case '3':
6291 case '4':
6292 case '5':
6293 case '6':
6294 case '7':
6295 case '8':
6296 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006297// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006298 num = atoi(name);
6299 if (num < 0 || num > shellparam.nparam)
6300 return -1;
6301 p = num ? shellparam.p[num - 1] : arg0;
6302 goto value;
6303 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006304 /* NB: name has form "VAR=..." */
6305
6306 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6307 * which should be considered before we check variables. */
6308 if (var_str_list) {
6309 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6310 p = NULL;
6311 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006312 char *str, *eq;
6313 str = var_str_list->text;
6314 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006315 if (!eq) /* stop at first non-assignment */
6316 break;
6317 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006318 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006319 && strncmp(str, name, name_len) == 0) {
6320 p = eq;
6321 /* goto value; - WRONG! */
6322 /* think "A=1 A=2 B=$A" */
6323 }
6324 var_str_list = var_str_list->next;
6325 } while (var_str_list);
6326 if (p)
6327 goto value;
6328 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329 p = lookupvar(name);
6330 value:
6331 if (!p)
6332 return -1;
6333
6334 len = strlen(p);
6335 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6336 memtodest(p, len, syntax, quotes);
6337 return len;
6338 }
6339
6340 if (subtype == VSPLUS || subtype == VSLENGTH)
6341 STADJUST(-len, expdest);
6342 return len;
6343}
6344
6345/*
6346 * Expand a variable, and return a pointer to the next character in the
6347 * input string.
6348 */
6349static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006350evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006351{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006352 char varflags;
6353 char subtype;
6354 char quoted;
6355 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356 char *var;
6357 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006358 int startloc;
6359 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006360
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006361 varflags = *p++;
6362 subtype = varflags & VSTYPE;
6363 quoted = varflags & VSQUOTE;
6364 var = p;
6365 easy = (!quoted || (*var == '@' && shellparam.nparam));
6366 startloc = expdest - (char *)stackblock();
6367 p = strchr(p, '=') + 1;
6368
6369 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006370 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006371 if (varflags & VSNUL)
6372 varlen--;
6373
6374 if (subtype == VSPLUS) {
6375 varlen = -1 - varlen;
6376 goto vsplus;
6377 }
6378
6379 if (subtype == VSMINUS) {
6380 vsplus:
6381 if (varlen < 0) {
6382 argstr(
6383 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006384 (quoted ? EXP_QWORD : EXP_WORD),
6385 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006386 );
6387 goto end;
6388 }
6389 if (easy)
6390 goto record;
6391 goto end;
6392 }
6393
6394 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6395 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006396 if (subevalvar(p, var, /* strloc: */ 0,
6397 subtype, startloc, varflags,
6398 /* quotes: */ 0,
6399 var_str_list)
6400 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006401 varflags &= ~VSNUL;
6402 /*
6403 * Remove any recorded regions beyond
6404 * start of variable
6405 */
6406 removerecordregions(startloc);
6407 goto again;
6408 }
6409 goto end;
6410 }
6411 if (easy)
6412 goto record;
6413 goto end;
6414 }
6415
6416 if (varlen < 0 && uflag)
6417 varunset(p, var, 0, 0);
6418
6419 if (subtype == VSLENGTH) {
6420 cvtnum(varlen > 0 ? varlen : 0);
6421 goto record;
6422 }
6423
6424 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006425 if (easy)
6426 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006427 goto end;
6428 }
6429
6430#if DEBUG
6431 switch (subtype) {
6432 case VSTRIMLEFT:
6433 case VSTRIMLEFTMAX:
6434 case VSTRIMRIGHT:
6435 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006436#if ENABLE_ASH_BASH_COMPAT
6437 case VSSUBSTR:
6438 case VSREPLACE:
6439 case VSREPLACEALL:
6440#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 break;
6442 default:
6443 abort();
6444 }
6445#endif
6446
6447 if (varlen >= 0) {
6448 /*
6449 * Terminate the string and start recording the pattern
6450 * right after it
6451 */
6452 STPUTC('\0', expdest);
6453 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006454 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6455 startloc, varflags,
6456 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6457 var_str_list)
6458 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006459 int amount = expdest - (
6460 (char *)stackblock() + patloc - 1
6461 );
6462 STADJUST(-amount, expdest);
6463 }
6464 /* Remove any recorded regions beyond start of variable */
6465 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006466 record:
6467 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006468 }
6469
6470 end:
6471 if (subtype != VSNORMAL) { /* skip to end of alternative */
6472 int nesting = 1;
6473 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006474 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006475 if (c == CTLESC)
6476 p++;
6477 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6478 if (varlen >= 0)
6479 argbackq = argbackq->next;
6480 } else if (c == CTLVAR) {
6481 if ((*p++ & VSTYPE) != VSNORMAL)
6482 nesting++;
6483 } else if (c == CTLENDVAR) {
6484 if (--nesting == 0)
6485 break;
6486 }
6487 }
6488 }
6489 return p;
6490}
6491
6492/*
6493 * Break the argument string into pieces based upon IFS and add the
6494 * strings to the argument list. The regions of the string to be
6495 * searched for IFS characters have been stored by recordregion.
6496 */
6497static void
6498ifsbreakup(char *string, struct arglist *arglist)
6499{
6500 struct ifsregion *ifsp;
6501 struct strlist *sp;
6502 char *start;
6503 char *p;
6504 char *q;
6505 const char *ifs, *realifs;
6506 int ifsspc;
6507 int nulonly;
6508
6509 start = string;
6510 if (ifslastp != NULL) {
6511 ifsspc = 0;
6512 nulonly = 0;
6513 realifs = ifsset() ? ifsval() : defifs;
6514 ifsp = &ifsfirst;
6515 do {
6516 p = string + ifsp->begoff;
6517 nulonly = ifsp->nulonly;
6518 ifs = nulonly ? nullstr : realifs;
6519 ifsspc = 0;
6520 while (p < string + ifsp->endoff) {
6521 q = p;
6522 if (*p == CTLESC)
6523 p++;
6524 if (!strchr(ifs, *p)) {
6525 p++;
6526 continue;
6527 }
6528 if (!nulonly)
6529 ifsspc = (strchr(defifs, *p) != NULL);
6530 /* Ignore IFS whitespace at start */
6531 if (q == start && ifsspc) {
6532 p++;
6533 start = p;
6534 continue;
6535 }
6536 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006537 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006538 sp->text = start;
6539 *arglist->lastp = sp;
6540 arglist->lastp = &sp->next;
6541 p++;
6542 if (!nulonly) {
6543 for (;;) {
6544 if (p >= string + ifsp->endoff) {
6545 break;
6546 }
6547 q = p;
6548 if (*p == CTLESC)
6549 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006550 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006551 p = q;
6552 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006553 }
6554 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006555 if (ifsspc) {
6556 p++;
6557 ifsspc = 0;
6558 } else {
6559 p = q;
6560 break;
6561 }
6562 } else
6563 p++;
6564 }
6565 }
6566 start = p;
6567 } /* while */
6568 ifsp = ifsp->next;
6569 } while (ifsp != NULL);
6570 if (nulonly)
6571 goto add;
6572 }
6573
6574 if (!*start)
6575 return;
6576
6577 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006578 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006579 sp->text = start;
6580 *arglist->lastp = sp;
6581 arglist->lastp = &sp->next;
6582}
6583
6584static void
6585ifsfree(void)
6586{
6587 struct ifsregion *p;
6588
6589 INT_OFF;
6590 p = ifsfirst.next;
6591 do {
6592 struct ifsregion *ifsp;
6593 ifsp = p->next;
6594 free(p);
6595 p = ifsp;
6596 } while (p);
6597 ifslastp = NULL;
6598 ifsfirst.next = NULL;
6599 INT_ON;
6600}
6601
6602/*
6603 * Add a file name to the list.
6604 */
6605static void
6606addfname(const char *name)
6607{
6608 struct strlist *sp;
6609
Denis Vlasenko597906c2008-02-20 16:38:54 +00006610 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006611 sp->text = ststrdup(name);
6612 *exparg.lastp = sp;
6613 exparg.lastp = &sp->next;
6614}
6615
6616static char *expdir;
6617
6618/*
6619 * Do metacharacter (i.e. *, ?, [...]) expansion.
6620 */
6621static void
6622expmeta(char *enddir, char *name)
6623{
6624 char *p;
6625 const char *cp;
6626 char *start;
6627 char *endname;
6628 int metaflag;
6629 struct stat statb;
6630 DIR *dirp;
6631 struct dirent *dp;
6632 int atend;
6633 int matchdot;
6634
6635 metaflag = 0;
6636 start = name;
6637 for (p = name; *p; p++) {
6638 if (*p == '*' || *p == '?')
6639 metaflag = 1;
6640 else if (*p == '[') {
6641 char *q = p + 1;
6642 if (*q == '!')
6643 q++;
6644 for (;;) {
6645 if (*q == '\\')
6646 q++;
6647 if (*q == '/' || *q == '\0')
6648 break;
6649 if (*++q == ']') {
6650 metaflag = 1;
6651 break;
6652 }
6653 }
6654 } else if (*p == '\\')
6655 p++;
6656 else if (*p == '/') {
6657 if (metaflag)
6658 goto out;
6659 start = p + 1;
6660 }
6661 }
6662 out:
6663 if (metaflag == 0) { /* we've reached the end of the file name */
6664 if (enddir != expdir)
6665 metaflag++;
6666 p = name;
6667 do {
6668 if (*p == '\\')
6669 p++;
6670 *enddir++ = *p;
6671 } while (*p++);
6672 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6673 addfname(expdir);
6674 return;
6675 }
6676 endname = p;
6677 if (name < start) {
6678 p = name;
6679 do {
6680 if (*p == '\\')
6681 p++;
6682 *enddir++ = *p++;
6683 } while (p < start);
6684 }
6685 if (enddir == expdir) {
6686 cp = ".";
6687 } else if (enddir == expdir + 1 && *expdir == '/') {
6688 cp = "/";
6689 } else {
6690 cp = expdir;
6691 enddir[-1] = '\0';
6692 }
6693 dirp = opendir(cp);
6694 if (dirp == NULL)
6695 return;
6696 if (enddir != expdir)
6697 enddir[-1] = '/';
6698 if (*endname == 0) {
6699 atend = 1;
6700 } else {
6701 atend = 0;
6702 *endname++ = '\0';
6703 }
6704 matchdot = 0;
6705 p = start;
6706 if (*p == '\\')
6707 p++;
6708 if (*p == '.')
6709 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006710 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006711 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006712 continue;
6713 if (pmatch(start, dp->d_name)) {
6714 if (atend) {
6715 strcpy(enddir, dp->d_name);
6716 addfname(expdir);
6717 } else {
6718 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6719 continue;
6720 p[-1] = '/';
6721 expmeta(p, endname);
6722 }
6723 }
6724 }
6725 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006726 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006727 endname[-1] = '/';
6728}
6729
6730static struct strlist *
6731msort(struct strlist *list, int len)
6732{
6733 struct strlist *p, *q = NULL;
6734 struct strlist **lpp;
6735 int half;
6736 int n;
6737
6738 if (len <= 1)
6739 return list;
6740 half = len >> 1;
6741 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006742 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006743 q = p;
6744 p = p->next;
6745 }
6746 q->next = NULL; /* terminate first half of list */
6747 q = msort(list, half); /* sort first half of list */
6748 p = msort(p, len - half); /* sort second half */
6749 lpp = &list;
6750 for (;;) {
6751#if ENABLE_LOCALE_SUPPORT
6752 if (strcoll(p->text, q->text) < 0)
6753#else
6754 if (strcmp(p->text, q->text) < 0)
6755#endif
6756 {
6757 *lpp = p;
6758 lpp = &p->next;
6759 p = *lpp;
6760 if (p == NULL) {
6761 *lpp = q;
6762 break;
6763 }
6764 } else {
6765 *lpp = q;
6766 lpp = &q->next;
6767 q = *lpp;
6768 if (q == NULL) {
6769 *lpp = p;
6770 break;
6771 }
6772 }
6773 }
6774 return list;
6775}
6776
6777/*
6778 * Sort the results of file name expansion. It calculates the number of
6779 * strings to sort and then calls msort (short for merge sort) to do the
6780 * work.
6781 */
6782static struct strlist *
6783expsort(struct strlist *str)
6784{
6785 int len;
6786 struct strlist *sp;
6787
6788 len = 0;
6789 for (sp = str; sp; sp = sp->next)
6790 len++;
6791 return msort(str, len);
6792}
6793
6794static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006795expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006796{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006797 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006798 '*', '?', '[', 0
6799 };
6800 /* TODO - EXP_REDIR */
6801
6802 while (str) {
6803 struct strlist **savelastp;
6804 struct strlist *sp;
6805 char *p;
6806
6807 if (fflag)
6808 goto nometa;
6809 if (!strpbrk(str->text, metachars))
6810 goto nometa;
6811 savelastp = exparg.lastp;
6812
6813 INT_OFF;
6814 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6815 {
6816 int i = strlen(str->text);
6817 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6818 }
6819
6820 expmeta(expdir, p);
6821 free(expdir);
6822 if (p != str->text)
6823 free(p);
6824 INT_ON;
6825 if (exparg.lastp == savelastp) {
6826 /*
6827 * no matches
6828 */
6829 nometa:
6830 *exparg.lastp = str;
6831 rmescapes(str->text);
6832 exparg.lastp = &str->next;
6833 } else {
6834 *exparg.lastp = NULL;
6835 *savelastp = sp = expsort(*savelastp);
6836 while (sp->next != NULL)
6837 sp = sp->next;
6838 exparg.lastp = &sp->next;
6839 }
6840 str = str->next;
6841 }
6842}
6843
6844/*
6845 * Perform variable substitution and command substitution on an argument,
6846 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6847 * perform splitting and file name expansion. When arglist is NULL, perform
6848 * here document expansion.
6849 */
6850static void
6851expandarg(union node *arg, struct arglist *arglist, int flag)
6852{
6853 struct strlist *sp;
6854 char *p;
6855
6856 argbackq = arg->narg.backquote;
6857 STARTSTACKSTR(expdest);
6858 ifsfirst.next = NULL;
6859 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006860 argstr(arg->narg.text, flag,
6861 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006862 p = _STPUTC('\0', expdest);
6863 expdest = p - 1;
6864 if (arglist == NULL) {
6865 return; /* here document expanded */
6866 }
6867 p = grabstackstr(p);
6868 exparg.lastp = &exparg.list;
6869 /*
6870 * TODO - EXP_REDIR
6871 */
6872 if (flag & EXP_FULL) {
6873 ifsbreakup(p, &exparg);
6874 *exparg.lastp = NULL;
6875 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006876 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006877 } else {
6878 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6879 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006880 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006881 sp->text = p;
6882 *exparg.lastp = sp;
6883 exparg.lastp = &sp->next;
6884 }
6885 if (ifsfirst.next)
6886 ifsfree();
6887 *exparg.lastp = NULL;
6888 if (exparg.list) {
6889 *arglist->lastp = exparg.list;
6890 arglist->lastp = exparg.lastp;
6891 }
6892}
6893
6894/*
6895 * Expand shell variables and backquotes inside a here document.
6896 */
6897static void
6898expandhere(union node *arg, int fd)
6899{
6900 herefd = fd;
6901 expandarg(arg, (struct arglist *)NULL, 0);
6902 full_write(fd, stackblock(), expdest - (char *)stackblock());
6903}
6904
6905/*
6906 * Returns true if the pattern matches the string.
6907 */
6908static int
6909patmatch(char *pattern, const char *string)
6910{
6911 return pmatch(preglob(pattern, 0, 0), string);
6912}
6913
6914/*
6915 * See if a pattern matches in a case statement.
6916 */
6917static int
6918casematch(union node *pattern, char *val)
6919{
6920 struct stackmark smark;
6921 int result;
6922
6923 setstackmark(&smark);
6924 argbackq = pattern->narg.backquote;
6925 STARTSTACKSTR(expdest);
6926 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006927 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6928 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006929 STACKSTRNUL(expdest);
6930 result = patmatch(stackblock(), val);
6931 popstackmark(&smark);
6932 return result;
6933}
6934
6935
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006936/* ============ find_command */
6937
6938struct builtincmd {
6939 const char *name;
6940 int (*builtin)(int, char **);
6941 /* unsigned flags; */
6942};
6943#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006944/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006945 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006946#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006947#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006948
6949struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006950 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006951 union param {
6952 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006953 /* index >= 0 for commands without path (slashes) */
6954 /* (TODO: what exactly does the value mean? PATH position?) */
6955 /* index == -1 for commands with slashes */
6956 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006957 const struct builtincmd *cmd;
6958 struct funcnode *func;
6959 } u;
6960};
6961/* values of cmdtype */
6962#define CMDUNKNOWN -1 /* no entry in table for command */
6963#define CMDNORMAL 0 /* command is an executable program */
6964#define CMDFUNCTION 1 /* command is a shell function */
6965#define CMDBUILTIN 2 /* command is a shell builtin */
6966
6967/* action to find_command() */
6968#define DO_ERR 0x01 /* prints errors */
6969#define DO_ABS 0x02 /* checks absolute paths */
6970#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6971#define DO_ALTPATH 0x08 /* using alternate path */
6972#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6973
6974static void find_command(char *, struct cmdentry *, int, const char *);
6975
6976
6977/* ============ Hashing commands */
6978
6979/*
6980 * When commands are first encountered, they are entered in a hash table.
6981 * This ensures that a full path search will not have to be done for them
6982 * on each invocation.
6983 *
6984 * We should investigate converting to a linear search, even though that
6985 * would make the command name "hash" a misnomer.
6986 */
6987
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006988struct tblentry {
6989 struct tblentry *next; /* next entry in hash chain */
6990 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00006991 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006992 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006993 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006994};
6995
Denis Vlasenko01631112007-12-16 17:20:38 +00006996static struct tblentry **cmdtable;
6997#define INIT_G_cmdtable() do { \
6998 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6999} while (0)
7000
7001static int builtinloc = -1; /* index in path of %builtin, or -1 */
7002
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007003
7004static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007005tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007006{
7007 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007008
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007009#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007010 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007011 if (APPLET_IS_NOEXEC(applet_no)) {
7012 while (*envp)
7013 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007014 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007015 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007016 /* re-exec ourselves with the new arguments */
7017 execve(bb_busybox_exec_path, argv, envp);
7018 /* If they called chroot or otherwise made the binary no longer
7019 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007020 }
7021#endif
7022
7023 repeat:
7024#ifdef SYSV
7025 do {
7026 execve(cmd, argv, envp);
7027 } while (errno == EINTR);
7028#else
7029 execve(cmd, argv, envp);
7030#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007031 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007032 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007033 return;
7034 }
7035 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007036 char **ap;
7037 char **new;
7038
7039 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007040 continue;
7041 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007042 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007043 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007044 ap += 2;
7045 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007046 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007047 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007048 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007049 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007050 goto repeat;
7051 }
7052}
7053
7054/*
7055 * Exec a program. Never returns. If you change this routine, you may
7056 * have to change the find_command routine as well.
7057 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007058static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007059static void
7060shellexec(char **argv, const char *path, int idx)
7061{
7062 char *cmdname;
7063 int e;
7064 char **envp;
7065 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007066#if ENABLE_FEATURE_SH_STANDALONE
7067 int applet_no = -1;
7068#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007069
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007070 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007071 envp = listvars(VEXPORT, VUNSET, 0);
7072 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007073#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007074 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007075#endif
7076 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007077 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007078 e = errno;
7079 } else {
7080 e = ENOENT;
7081 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7082 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007083 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007084 if (errno != ENOENT && errno != ENOTDIR)
7085 e = errno;
7086 }
7087 stunalloc(cmdname);
7088 }
7089 }
7090
7091 /* Map to POSIX errors */
7092 switch (e) {
7093 case EACCES:
7094 exerrno = 126;
7095 break;
7096 case ENOENT:
7097 exerrno = 127;
7098 break;
7099 default:
7100 exerrno = 2;
7101 break;
7102 }
7103 exitstatus = exerrno;
7104 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007105 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007106 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7107 /* NOTREACHED */
7108}
7109
7110static void
7111printentry(struct tblentry *cmdp)
7112{
7113 int idx;
7114 const char *path;
7115 char *name;
7116
7117 idx = cmdp->param.index;
7118 path = pathval();
7119 do {
7120 name = padvance(&path, cmdp->cmdname);
7121 stunalloc(name);
7122 } while (--idx >= 0);
7123 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7124}
7125
7126/*
7127 * Clear out command entries. The argument specifies the first entry in
7128 * PATH which has changed.
7129 */
7130static void
7131clearcmdentry(int firstchange)
7132{
7133 struct tblentry **tblp;
7134 struct tblentry **pp;
7135 struct tblentry *cmdp;
7136
7137 INT_OFF;
7138 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7139 pp = tblp;
7140 while ((cmdp = *pp) != NULL) {
7141 if ((cmdp->cmdtype == CMDNORMAL &&
7142 cmdp->param.index >= firstchange)
7143 || (cmdp->cmdtype == CMDBUILTIN &&
7144 builtinloc >= firstchange)
7145 ) {
7146 *pp = cmdp->next;
7147 free(cmdp);
7148 } else {
7149 pp = &cmdp->next;
7150 }
7151 }
7152 }
7153 INT_ON;
7154}
7155
7156/*
7157 * Locate a command in the command hash table. If "add" is nonzero,
7158 * add the command to the table if it is not already present. The
7159 * variable "lastcmdentry" is set to point to the address of the link
7160 * pointing to the entry, so that delete_cmd_entry can delete the
7161 * entry.
7162 *
7163 * Interrupts must be off if called with add != 0.
7164 */
7165static struct tblentry **lastcmdentry;
7166
7167static struct tblentry *
7168cmdlookup(const char *name, int add)
7169{
7170 unsigned int hashval;
7171 const char *p;
7172 struct tblentry *cmdp;
7173 struct tblentry **pp;
7174
7175 p = name;
7176 hashval = (unsigned char)*p << 4;
7177 while (*p)
7178 hashval += (unsigned char)*p++;
7179 hashval &= 0x7FFF;
7180 pp = &cmdtable[hashval % CMDTABLESIZE];
7181 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7182 if (strcmp(cmdp->cmdname, name) == 0)
7183 break;
7184 pp = &cmdp->next;
7185 }
7186 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007187 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7188 + strlen(name)
7189 /* + 1 - already done because
7190 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007191 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007192 cmdp->cmdtype = CMDUNKNOWN;
7193 strcpy(cmdp->cmdname, name);
7194 }
7195 lastcmdentry = pp;
7196 return cmdp;
7197}
7198
7199/*
7200 * Delete the command entry returned on the last lookup.
7201 */
7202static void
7203delete_cmd_entry(void)
7204{
7205 struct tblentry *cmdp;
7206
7207 INT_OFF;
7208 cmdp = *lastcmdentry;
7209 *lastcmdentry = cmdp->next;
7210 if (cmdp->cmdtype == CMDFUNCTION)
7211 freefunc(cmdp->param.func);
7212 free(cmdp);
7213 INT_ON;
7214}
7215
7216/*
7217 * Add a new command entry, replacing any existing command entry for
7218 * the same name - except special builtins.
7219 */
7220static void
7221addcmdentry(char *name, struct cmdentry *entry)
7222{
7223 struct tblentry *cmdp;
7224
7225 cmdp = cmdlookup(name, 1);
7226 if (cmdp->cmdtype == CMDFUNCTION) {
7227 freefunc(cmdp->param.func);
7228 }
7229 cmdp->cmdtype = entry->cmdtype;
7230 cmdp->param = entry->u;
7231 cmdp->rehash = 0;
7232}
7233
7234static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007235hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007236{
7237 struct tblentry **pp;
7238 struct tblentry *cmdp;
7239 int c;
7240 struct cmdentry entry;
7241 char *name;
7242
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007243 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007244 clearcmdentry(0);
7245 return 0;
7246 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007247
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007248 if (*argptr == NULL) {
7249 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7250 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7251 if (cmdp->cmdtype == CMDNORMAL)
7252 printentry(cmdp);
7253 }
7254 }
7255 return 0;
7256 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007257
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007258 c = 0;
7259 while ((name = *argptr) != NULL) {
7260 cmdp = cmdlookup(name, 0);
7261 if (cmdp != NULL
7262 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007263 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7264 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007265 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007266 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007267 find_command(name, &entry, DO_ERR, pathval());
7268 if (entry.cmdtype == CMDUNKNOWN)
7269 c = 1;
7270 argptr++;
7271 }
7272 return c;
7273}
7274
7275/*
7276 * Called when a cd is done. Marks all commands so the next time they
7277 * are executed they will be rehashed.
7278 */
7279static void
7280hashcd(void)
7281{
7282 struct tblentry **pp;
7283 struct tblentry *cmdp;
7284
7285 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7286 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007287 if (cmdp->cmdtype == CMDNORMAL
7288 || (cmdp->cmdtype == CMDBUILTIN
7289 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7290 && builtinloc > 0)
7291 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007292 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007293 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007294 }
7295 }
7296}
7297
7298/*
7299 * Fix command hash table when PATH changed.
7300 * Called before PATH is changed. The argument is the new value of PATH;
7301 * pathval() still returns the old value at this point.
7302 * Called with interrupts off.
7303 */
7304static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007305changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007306{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007307 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007308 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007309 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007310 int idx_bltin;
7311
7312 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007313 firstchange = 9999; /* assume no change */
7314 idx = 0;
7315 idx_bltin = -1;
7316 for (;;) {
7317 if (*old != *new) {
7318 firstchange = idx;
7319 if ((*old == '\0' && *new == ':')
7320 || (*old == ':' && *new == '\0'))
7321 firstchange++;
7322 old = new; /* ignore subsequent differences */
7323 }
7324 if (*new == '\0')
7325 break;
7326 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7327 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007328 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007329 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007330 new++, old++;
7331 }
7332 if (builtinloc < 0 && idx_bltin >= 0)
7333 builtinloc = idx_bltin; /* zap builtins */
7334 if (builtinloc >= 0 && idx_bltin < 0)
7335 firstchange = 0;
7336 clearcmdentry(firstchange);
7337 builtinloc = idx_bltin;
7338}
7339
7340#define TEOF 0
7341#define TNL 1
7342#define TREDIR 2
7343#define TWORD 3
7344#define TSEMI 4
7345#define TBACKGND 5
7346#define TAND 6
7347#define TOR 7
7348#define TPIPE 8
7349#define TLP 9
7350#define TRP 10
7351#define TENDCASE 11
7352#define TENDBQUOTE 12
7353#define TNOT 13
7354#define TCASE 14
7355#define TDO 15
7356#define TDONE 16
7357#define TELIF 17
7358#define TELSE 18
7359#define TESAC 19
7360#define TFI 20
7361#define TFOR 21
7362#define TIF 22
7363#define TIN 23
7364#define TTHEN 24
7365#define TUNTIL 25
7366#define TWHILE 26
7367#define TBEGIN 27
7368#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007369typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007370
7371/* first char is indicating which tokens mark the end of a list */
7372static const char *const tokname_array[] = {
7373 "\1end of file",
7374 "\0newline",
7375 "\0redirection",
7376 "\0word",
7377 "\0;",
7378 "\0&",
7379 "\0&&",
7380 "\0||",
7381 "\0|",
7382 "\0(",
7383 "\1)",
7384 "\1;;",
7385 "\1`",
7386#define KWDOFFSET 13
7387 /* the following are keywords */
7388 "\0!",
7389 "\0case",
7390 "\1do",
7391 "\1done",
7392 "\1elif",
7393 "\1else",
7394 "\1esac",
7395 "\1fi",
7396 "\0for",
7397 "\0if",
7398 "\0in",
7399 "\1then",
7400 "\0until",
7401 "\0while",
7402 "\0{",
7403 "\1}",
7404};
7405
7406static const char *
7407tokname(int tok)
7408{
7409 static char buf[16];
7410
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007411//try this:
7412//if (tok < TSEMI) return tokname_array[tok] + 1;
7413//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7414//return buf;
7415
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007416 if (tok >= TSEMI)
7417 buf[0] = '"';
7418 sprintf(buf + (tok >= TSEMI), "%s%c",
7419 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7420 return buf;
7421}
7422
7423/* Wrapper around strcmp for qsort/bsearch/... */
7424static int
7425pstrcmp(const void *a, const void *b)
7426{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007427 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007428}
7429
7430static const char *const *
7431findkwd(const char *s)
7432{
7433 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007434 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7435 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007436}
7437
7438/*
7439 * Locate and print what a word is...
7440 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007441static int
7442describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007443{
7444 struct cmdentry entry;
7445 struct tblentry *cmdp;
7446#if ENABLE_ASH_ALIAS
7447 const struct alias *ap;
7448#endif
7449 const char *path = pathval();
7450
7451 if (describe_command_verbose) {
7452 out1str(command);
7453 }
7454
7455 /* First look at the keywords */
7456 if (findkwd(command)) {
7457 out1str(describe_command_verbose ? " is a shell keyword" : command);
7458 goto out;
7459 }
7460
7461#if ENABLE_ASH_ALIAS
7462 /* Then look at the aliases */
7463 ap = lookupalias(command, 0);
7464 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007465 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007466 out1str("alias ");
7467 printalias(ap);
7468 return 0;
7469 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007470 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007471 goto out;
7472 }
7473#endif
7474 /* Then check if it is a tracked alias */
7475 cmdp = cmdlookup(command, 0);
7476 if (cmdp != NULL) {
7477 entry.cmdtype = cmdp->cmdtype;
7478 entry.u = cmdp->param;
7479 } else {
7480 /* Finally use brute force */
7481 find_command(command, &entry, DO_ABS, path);
7482 }
7483
7484 switch (entry.cmdtype) {
7485 case CMDNORMAL: {
7486 int j = entry.u.index;
7487 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007488 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007489 p = command;
7490 } else {
7491 do {
7492 p = padvance(&path, command);
7493 stunalloc(p);
7494 } while (--j >= 0);
7495 }
7496 if (describe_command_verbose) {
7497 out1fmt(" is%s %s",
7498 (cmdp ? " a tracked alias for" : nullstr), p
7499 );
7500 } else {
7501 out1str(p);
7502 }
7503 break;
7504 }
7505
7506 case CMDFUNCTION:
7507 if (describe_command_verbose) {
7508 out1str(" is a shell function");
7509 } else {
7510 out1str(command);
7511 }
7512 break;
7513
7514 case CMDBUILTIN:
7515 if (describe_command_verbose) {
7516 out1fmt(" is a %sshell builtin",
7517 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7518 "special " : nullstr
7519 );
7520 } else {
7521 out1str(command);
7522 }
7523 break;
7524
7525 default:
7526 if (describe_command_verbose) {
7527 out1str(": not found\n");
7528 }
7529 return 127;
7530 }
7531 out:
7532 outstr("\n", stdout);
7533 return 0;
7534}
7535
7536static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007537typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007538{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007539 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007540 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007541 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007542
Denis Vlasenko46846e22007-05-20 13:08:31 +00007543 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007544 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007545 i++;
7546 verbose = 0;
7547 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007548 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007549 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007550 }
7551 return err;
7552}
7553
7554#if ENABLE_ASH_CMDCMD
7555static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007556commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007557{
7558 int c;
7559 enum {
7560 VERIFY_BRIEF = 1,
7561 VERIFY_VERBOSE = 2,
7562 } verify = 0;
7563
7564 while ((c = nextopt("pvV")) != '\0')
7565 if (c == 'V')
7566 verify |= VERIFY_VERBOSE;
7567 else if (c == 'v')
7568 verify |= VERIFY_BRIEF;
7569#if DEBUG
7570 else if (c != 'p')
7571 abort();
7572#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007573 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7574 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007575 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007576 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007577
7578 return 0;
7579}
7580#endif
7581
7582
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007583/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007584
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007585static int funcblocksize; /* size of structures in function */
7586static int funcstringsize; /* size of strings in node */
7587static void *funcblock; /* block to allocate function from */
7588static char *funcstring; /* block to allocate strings from */
7589
Eric Andersencb57d552001-06-28 07:25:16 +00007590/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007591#define EV_EXIT 01 /* exit after evaluating tree */
7592#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7593#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007594
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007595static const short nodesize[26] = {
7596 SHELL_ALIGN(sizeof(struct ncmd)),
7597 SHELL_ALIGN(sizeof(struct npipe)),
7598 SHELL_ALIGN(sizeof(struct nredir)),
7599 SHELL_ALIGN(sizeof(struct nredir)),
7600 SHELL_ALIGN(sizeof(struct nredir)),
7601 SHELL_ALIGN(sizeof(struct nbinary)),
7602 SHELL_ALIGN(sizeof(struct nbinary)),
7603 SHELL_ALIGN(sizeof(struct nbinary)),
7604 SHELL_ALIGN(sizeof(struct nif)),
7605 SHELL_ALIGN(sizeof(struct nbinary)),
7606 SHELL_ALIGN(sizeof(struct nbinary)),
7607 SHELL_ALIGN(sizeof(struct nfor)),
7608 SHELL_ALIGN(sizeof(struct ncase)),
7609 SHELL_ALIGN(sizeof(struct nclist)),
7610 SHELL_ALIGN(sizeof(struct narg)),
7611 SHELL_ALIGN(sizeof(struct narg)),
7612 SHELL_ALIGN(sizeof(struct nfile)),
7613 SHELL_ALIGN(sizeof(struct nfile)),
7614 SHELL_ALIGN(sizeof(struct nfile)),
7615 SHELL_ALIGN(sizeof(struct nfile)),
7616 SHELL_ALIGN(sizeof(struct nfile)),
7617 SHELL_ALIGN(sizeof(struct ndup)),
7618 SHELL_ALIGN(sizeof(struct ndup)),
7619 SHELL_ALIGN(sizeof(struct nhere)),
7620 SHELL_ALIGN(sizeof(struct nhere)),
7621 SHELL_ALIGN(sizeof(struct nnot)),
7622};
7623
7624static void calcsize(union node *n);
7625
7626static void
7627sizenodelist(struct nodelist *lp)
7628{
7629 while (lp) {
7630 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7631 calcsize(lp->n);
7632 lp = lp->next;
7633 }
7634}
7635
7636static void
7637calcsize(union node *n)
7638{
7639 if (n == NULL)
7640 return;
7641 funcblocksize += nodesize[n->type];
7642 switch (n->type) {
7643 case NCMD:
7644 calcsize(n->ncmd.redirect);
7645 calcsize(n->ncmd.args);
7646 calcsize(n->ncmd.assign);
7647 break;
7648 case NPIPE:
7649 sizenodelist(n->npipe.cmdlist);
7650 break;
7651 case NREDIR:
7652 case NBACKGND:
7653 case NSUBSHELL:
7654 calcsize(n->nredir.redirect);
7655 calcsize(n->nredir.n);
7656 break;
7657 case NAND:
7658 case NOR:
7659 case NSEMI:
7660 case NWHILE:
7661 case NUNTIL:
7662 calcsize(n->nbinary.ch2);
7663 calcsize(n->nbinary.ch1);
7664 break;
7665 case NIF:
7666 calcsize(n->nif.elsepart);
7667 calcsize(n->nif.ifpart);
7668 calcsize(n->nif.test);
7669 break;
7670 case NFOR:
7671 funcstringsize += strlen(n->nfor.var) + 1;
7672 calcsize(n->nfor.body);
7673 calcsize(n->nfor.args);
7674 break;
7675 case NCASE:
7676 calcsize(n->ncase.cases);
7677 calcsize(n->ncase.expr);
7678 break;
7679 case NCLIST:
7680 calcsize(n->nclist.body);
7681 calcsize(n->nclist.pattern);
7682 calcsize(n->nclist.next);
7683 break;
7684 case NDEFUN:
7685 case NARG:
7686 sizenodelist(n->narg.backquote);
7687 funcstringsize += strlen(n->narg.text) + 1;
7688 calcsize(n->narg.next);
7689 break;
7690 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007691#if ENABLE_ASH_BASH_COMPAT
7692 case NTO2:
7693#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007694 case NCLOBBER:
7695 case NFROM:
7696 case NFROMTO:
7697 case NAPPEND:
7698 calcsize(n->nfile.fname);
7699 calcsize(n->nfile.next);
7700 break;
7701 case NTOFD:
7702 case NFROMFD:
7703 calcsize(n->ndup.vname);
7704 calcsize(n->ndup.next);
7705 break;
7706 case NHERE:
7707 case NXHERE:
7708 calcsize(n->nhere.doc);
7709 calcsize(n->nhere.next);
7710 break;
7711 case NNOT:
7712 calcsize(n->nnot.com);
7713 break;
7714 };
7715}
7716
7717static char *
7718nodeckstrdup(char *s)
7719{
7720 char *rtn = funcstring;
7721
7722 strcpy(funcstring, s);
7723 funcstring += strlen(s) + 1;
7724 return rtn;
7725}
7726
7727static union node *copynode(union node *);
7728
7729static struct nodelist *
7730copynodelist(struct nodelist *lp)
7731{
7732 struct nodelist *start;
7733 struct nodelist **lpp;
7734
7735 lpp = &start;
7736 while (lp) {
7737 *lpp = funcblock;
7738 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7739 (*lpp)->n = copynode(lp->n);
7740 lp = lp->next;
7741 lpp = &(*lpp)->next;
7742 }
7743 *lpp = NULL;
7744 return start;
7745}
7746
7747static union node *
7748copynode(union node *n)
7749{
7750 union node *new;
7751
7752 if (n == NULL)
7753 return NULL;
7754 new = funcblock;
7755 funcblock = (char *) funcblock + nodesize[n->type];
7756
7757 switch (n->type) {
7758 case NCMD:
7759 new->ncmd.redirect = copynode(n->ncmd.redirect);
7760 new->ncmd.args = copynode(n->ncmd.args);
7761 new->ncmd.assign = copynode(n->ncmd.assign);
7762 break;
7763 case NPIPE:
7764 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007765 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007766 break;
7767 case NREDIR:
7768 case NBACKGND:
7769 case NSUBSHELL:
7770 new->nredir.redirect = copynode(n->nredir.redirect);
7771 new->nredir.n = copynode(n->nredir.n);
7772 break;
7773 case NAND:
7774 case NOR:
7775 case NSEMI:
7776 case NWHILE:
7777 case NUNTIL:
7778 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7779 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7780 break;
7781 case NIF:
7782 new->nif.elsepart = copynode(n->nif.elsepart);
7783 new->nif.ifpart = copynode(n->nif.ifpart);
7784 new->nif.test = copynode(n->nif.test);
7785 break;
7786 case NFOR:
7787 new->nfor.var = nodeckstrdup(n->nfor.var);
7788 new->nfor.body = copynode(n->nfor.body);
7789 new->nfor.args = copynode(n->nfor.args);
7790 break;
7791 case NCASE:
7792 new->ncase.cases = copynode(n->ncase.cases);
7793 new->ncase.expr = copynode(n->ncase.expr);
7794 break;
7795 case NCLIST:
7796 new->nclist.body = copynode(n->nclist.body);
7797 new->nclist.pattern = copynode(n->nclist.pattern);
7798 new->nclist.next = copynode(n->nclist.next);
7799 break;
7800 case NDEFUN:
7801 case NARG:
7802 new->narg.backquote = copynodelist(n->narg.backquote);
7803 new->narg.text = nodeckstrdup(n->narg.text);
7804 new->narg.next = copynode(n->narg.next);
7805 break;
7806 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007807#if ENABLE_ASH_BASH_COMPAT
7808 case NTO2:
7809#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007810 case NCLOBBER:
7811 case NFROM:
7812 case NFROMTO:
7813 case NAPPEND:
7814 new->nfile.fname = copynode(n->nfile.fname);
7815 new->nfile.fd = n->nfile.fd;
7816 new->nfile.next = copynode(n->nfile.next);
7817 break;
7818 case NTOFD:
7819 case NFROMFD:
7820 new->ndup.vname = copynode(n->ndup.vname);
7821 new->ndup.dupfd = n->ndup.dupfd;
7822 new->ndup.fd = n->ndup.fd;
7823 new->ndup.next = copynode(n->ndup.next);
7824 break;
7825 case NHERE:
7826 case NXHERE:
7827 new->nhere.doc = copynode(n->nhere.doc);
7828 new->nhere.fd = n->nhere.fd;
7829 new->nhere.next = copynode(n->nhere.next);
7830 break;
7831 case NNOT:
7832 new->nnot.com = copynode(n->nnot.com);
7833 break;
7834 };
7835 new->type = n->type;
7836 return new;
7837}
7838
7839/*
7840 * Make a copy of a parse tree.
7841 */
7842static struct funcnode *
7843copyfunc(union node *n)
7844{
7845 struct funcnode *f;
7846 size_t blocksize;
7847
7848 funcblocksize = offsetof(struct funcnode, n);
7849 funcstringsize = 0;
7850 calcsize(n);
7851 blocksize = funcblocksize;
7852 f = ckmalloc(blocksize + funcstringsize);
7853 funcblock = (char *) f + offsetof(struct funcnode, n);
7854 funcstring = (char *) f + blocksize;
7855 copynode(n);
7856 f->count = 0;
7857 return f;
7858}
7859
7860/*
7861 * Define a shell function.
7862 */
7863static void
7864defun(char *name, union node *func)
7865{
7866 struct cmdentry entry;
7867
7868 INT_OFF;
7869 entry.cmdtype = CMDFUNCTION;
7870 entry.u.func = copyfunc(func);
7871 addcmdentry(name, &entry);
7872 INT_ON;
7873}
7874
7875static int evalskip; /* set if we are skipping commands */
7876/* reasons for skipping commands (see comment on breakcmd routine) */
7877#define SKIPBREAK (1 << 0)
7878#define SKIPCONT (1 << 1)
7879#define SKIPFUNC (1 << 2)
7880#define SKIPFILE (1 << 3)
7881#define SKIPEVAL (1 << 4)
7882static int skipcount; /* number of levels to skip */
7883static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007884static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007885
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007886/* forward decl way out to parsing code - dotrap needs it */
7887static int evalstring(char *s, int mask);
7888
7889/*
7890 * Called to execute a trap. Perhaps we should avoid entering new trap
7891 * handlers while we are executing a trap handler.
7892 */
7893static int
7894dotrap(void)
7895{
7896 char *p;
7897 char *q;
7898 int i;
7899 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007900 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007901
7902 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007903 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007904 xbarrier();
7905
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007906 for (i = 1, q = gotsig; i < NSIG; i++, q++) {
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007907 if (!*q)
7908 continue;
7909 *q = '\0';
7910
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007911 p = trap[i];
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007912 if (!p)
7913 continue;
7914 skip = evalstring(p, SKIPEVAL);
7915 exitstatus = savestatus;
7916 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007917 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007918 }
7919
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007920 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007921}
7922
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007923/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007924static void evalloop(union node *, int);
7925static void evalfor(union node *, int);
7926static void evalcase(union node *, int);
7927static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007928static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007929static void evalpipe(union node *, int);
7930static void evalcommand(union node *, int);
7931static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007932static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007933
Eric Andersen62483552001-07-10 06:09:16 +00007934/*
Eric Andersenc470f442003-07-28 09:56:35 +00007935 * Evaluate a parse tree. The value is left in the global variable
7936 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007937 */
Eric Andersenc470f442003-07-28 09:56:35 +00007938static void
7939evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007940{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007941
7942 struct jmploc *volatile savehandler = exception_handler;
7943 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00007944 int checkexit = 0;
7945 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007946 int status;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007947
Eric Andersenc470f442003-07-28 09:56:35 +00007948 if (n == NULL) {
7949 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007950 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00007951 }
Eric Andersenc470f442003-07-28 09:56:35 +00007952 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007953 getpid(), n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00007954
7955 exception_handler = &jmploc;
7956 {
7957 int err = setjmp(jmploc.loc);
7958 if (err) {
7959 /* if it was a signal, check for trap handlers */
7960 if (exception == EXSIG)
7961 goto out;
7962 /* continue on the way out */
7963 exception_handler = savehandler;
7964 longjmp(exception_handler->loc, err);
7965 }
7966 }
7967
Eric Andersenc470f442003-07-28 09:56:35 +00007968 switch (n->type) {
7969 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007970#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007971 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007972 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007973 break;
7974#endif
7975 case NNOT:
7976 evaltree(n->nnot.com, EV_TESTED);
7977 status = !exitstatus;
7978 goto setstatus;
7979 case NREDIR:
7980 expredir(n->nredir.redirect);
7981 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7982 if (!status) {
7983 evaltree(n->nredir.n, flags & EV_TESTED);
7984 status = exitstatus;
7985 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007986 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00007987 goto setstatus;
7988 case NCMD:
7989 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007990 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007991 if (eflag && !(flags & EV_TESTED))
7992 checkexit = ~0;
7993 goto calleval;
7994 case NFOR:
7995 evalfn = evalfor;
7996 goto calleval;
7997 case NWHILE:
7998 case NUNTIL:
7999 evalfn = evalloop;
8000 goto calleval;
8001 case NSUBSHELL:
8002 case NBACKGND:
8003 evalfn = evalsubshell;
8004 goto calleval;
8005 case NPIPE:
8006 evalfn = evalpipe;
8007 goto checkexit;
8008 case NCASE:
8009 evalfn = evalcase;
8010 goto calleval;
8011 case NAND:
8012 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008013 case NSEMI: {
8014
Eric Andersenc470f442003-07-28 09:56:35 +00008015#if NAND + 1 != NOR
8016#error NAND + 1 != NOR
8017#endif
8018#if NOR + 1 != NSEMI
8019#error NOR + 1 != NSEMI
8020#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008021 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008022 evaltree(
8023 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008024 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008025 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008026 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008027 break;
8028 if (!evalskip) {
8029 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008030 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008031 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008032 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008033 evalfn(n, flags);
8034 break;
8035 }
8036 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008037 }
Eric Andersenc470f442003-07-28 09:56:35 +00008038 case NIF:
8039 evaltree(n->nif.test, EV_TESTED);
8040 if (evalskip)
8041 break;
8042 if (exitstatus == 0) {
8043 n = n->nif.ifpart;
8044 goto evaln;
8045 } else if (n->nif.elsepart) {
8046 n = n->nif.elsepart;
8047 goto evaln;
8048 }
8049 goto success;
8050 case NDEFUN:
8051 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008052 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008053 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008054 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008055 exitstatus = status;
8056 break;
8057 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008058
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008059 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008060 exception_handler = savehandler;
8061 out1:
8062 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008063 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008064 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008065 goto exexit;
8066
8067 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008068 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008069 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008070 }
Eric Andersen62483552001-07-10 06:09:16 +00008071}
8072
Eric Andersenc470f442003-07-28 09:56:35 +00008073#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8074static
8075#endif
8076void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8077
Eric Andersenc470f442003-07-28 09:56:35 +00008078static void
8079evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008080{
8081 int status;
8082
8083 loopnest++;
8084 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008085 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008086 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008087 int i;
8088
Eric Andersencb57d552001-06-28 07:25:16 +00008089 evaltree(n->nbinary.ch1, EV_TESTED);
8090 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008091 skipping:
8092 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008093 evalskip = 0;
8094 continue;
8095 }
8096 if (evalskip == SKIPBREAK && --skipcount <= 0)
8097 evalskip = 0;
8098 break;
8099 }
Eric Andersenc470f442003-07-28 09:56:35 +00008100 i = exitstatus;
8101 if (n->type != NWHILE)
8102 i = !i;
8103 if (i != 0)
8104 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008105 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008106 status = exitstatus;
8107 if (evalskip)
8108 goto skipping;
8109 }
8110 loopnest--;
8111 exitstatus = status;
8112}
8113
Eric Andersenc470f442003-07-28 09:56:35 +00008114static void
8115evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008116{
8117 struct arglist arglist;
8118 union node *argp;
8119 struct strlist *sp;
8120 struct stackmark smark;
8121
8122 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008123 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008124 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008125 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008126 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008127 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008128 if (evalskip)
8129 goto out;
8130 }
8131 *arglist.lastp = NULL;
8132
8133 exitstatus = 0;
8134 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008135 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008136 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008137 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008138 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008139 if (evalskip) {
8140 if (evalskip == SKIPCONT && --skipcount <= 0) {
8141 evalskip = 0;
8142 continue;
8143 }
8144 if (evalskip == SKIPBREAK && --skipcount <= 0)
8145 evalskip = 0;
8146 break;
8147 }
8148 }
8149 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008150 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008151 popstackmark(&smark);
8152}
8153
Eric Andersenc470f442003-07-28 09:56:35 +00008154static void
8155evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008156{
8157 union node *cp;
8158 union node *patp;
8159 struct arglist arglist;
8160 struct stackmark smark;
8161
8162 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008163 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008164 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008165 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008166 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008167 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8168 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008169 if (casematch(patp, arglist.list->text)) {
8170 if (evalskip == 0) {
8171 evaltree(cp->nclist.body, flags);
8172 }
8173 goto out;
8174 }
8175 }
8176 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008177 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008178 popstackmark(&smark);
8179}
8180
Eric Andersenc470f442003-07-28 09:56:35 +00008181/*
8182 * Kick off a subshell to evaluate a tree.
8183 */
Eric Andersenc470f442003-07-28 09:56:35 +00008184static void
8185evalsubshell(union node *n, int flags)
8186{
8187 struct job *jp;
8188 int backgnd = (n->type == NBACKGND);
8189 int status;
8190
8191 expredir(n->nredir.redirect);
8192 if (!backgnd && flags & EV_EXIT && !trap[0])
8193 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008194 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008195 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008196 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008197 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008198 flags |= EV_EXIT;
8199 if (backgnd)
8200 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008201 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008202 redirect(n->nredir.redirect, 0);
8203 evaltreenr(n->nredir.n, flags);
8204 /* never returns */
8205 }
8206 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008207 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008208 status = waitforjob(jp);
8209 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008210 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008211}
8212
Eric Andersenc470f442003-07-28 09:56:35 +00008213/*
8214 * Compute the names of the files in a redirection list.
8215 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008216static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008217static void
8218expredir(union node *n)
8219{
8220 union node *redir;
8221
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008222 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008223 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008224
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008225 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008226 fn.lastp = &fn.list;
8227 switch (redir->type) {
8228 case NFROMTO:
8229 case NFROM:
8230 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008231#if ENABLE_ASH_BASH_COMPAT
8232 case NTO2:
8233#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008234 case NCLOBBER:
8235 case NAPPEND:
8236 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008237#if ENABLE_ASH_BASH_COMPAT
8238 store_expfname:
8239#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008240 redir->nfile.expfname = fn.list->text;
8241 break;
8242 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008243 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008244 if (redir->ndup.vname) {
8245 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008246 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008247 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008248#if ENABLE_ASH_BASH_COMPAT
8249//FIXME: we used expandarg with different args!
8250 if (!isdigit_str9(fn.list->text)) {
8251 /* >&file, not >&fd */
8252 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8253 ash_msg_and_raise_error("redir error");
8254 redir->type = NTO2;
8255 goto store_expfname;
8256 }
8257#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008258 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008259 }
8260 break;
8261 }
8262 }
8263}
8264
Eric Andersencb57d552001-06-28 07:25:16 +00008265/*
Eric Andersencb57d552001-06-28 07:25:16 +00008266 * Evaluate a pipeline. All the processes in the pipeline are children
8267 * of the process creating the pipeline. (This differs from some versions
8268 * of the shell, which make the last process in a pipeline the parent
8269 * of all the rest.)
8270 */
Eric Andersenc470f442003-07-28 09:56:35 +00008271static void
8272evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008273{
8274 struct job *jp;
8275 struct nodelist *lp;
8276 int pipelen;
8277 int prevfd;
8278 int pip[2];
8279
Eric Andersenc470f442003-07-28 09:56:35 +00008280 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008281 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008282 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008283 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008284 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008285 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008286 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008287 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008288 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008289 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008290 pip[1] = -1;
8291 if (lp->next) {
8292 if (pipe(pip) < 0) {
8293 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008294 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008295 }
8296 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008297 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008298 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008299 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008300 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008301 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008302 if (prevfd > 0) {
8303 dup2(prevfd, 0);
8304 close(prevfd);
8305 }
8306 if (pip[1] > 1) {
8307 dup2(pip[1], 1);
8308 close(pip[1]);
8309 }
Eric Andersenc470f442003-07-28 09:56:35 +00008310 evaltreenr(lp->n, flags);
8311 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008312 }
8313 if (prevfd >= 0)
8314 close(prevfd);
8315 prevfd = pip[0];
8316 close(pip[1]);
8317 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008318 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008319 exitstatus = waitforjob(jp);
8320 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008321 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008322 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008323}
8324
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008325/*
8326 * Controls whether the shell is interactive or not.
8327 */
8328static void
8329setinteractive(int on)
8330{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008331 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008332
8333 if (++on == is_interactive)
8334 return;
8335 is_interactive = on;
8336 setsignal(SIGINT);
8337 setsignal(SIGQUIT);
8338 setsignal(SIGTERM);
8339#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8340 if (is_interactive > 1) {
8341 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008342 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008343
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008344 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008345 out1fmt(
8346 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008347 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008348 "Enter 'help' for a list of built-in commands."
8349 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008350 bb_banner);
8351 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008352 }
8353 }
8354#endif
8355}
8356
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008357static void
8358optschanged(void)
8359{
8360#if DEBUG
8361 opentrace();
8362#endif
8363 setinteractive(iflag);
8364 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008365#if ENABLE_FEATURE_EDITING_VI
8366 if (viflag)
8367 line_input_state->flags |= VI_MODE;
8368 else
8369 line_input_state->flags &= ~VI_MODE;
8370#else
8371 viflag = 0; /* forcibly keep the option off */
8372#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008373}
8374
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008375static struct localvar *localvars;
8376
8377/*
8378 * Called after a function returns.
8379 * Interrupts must be off.
8380 */
8381static void
8382poplocalvars(void)
8383{
8384 struct localvar *lvp;
8385 struct var *vp;
8386
8387 while ((lvp = localvars) != NULL) {
8388 localvars = lvp->next;
8389 vp = lvp->vp;
8390 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8391 if (vp == NULL) { /* $- saved */
8392 memcpy(optlist, lvp->text, sizeof(optlist));
8393 free((char*)lvp->text);
8394 optschanged();
8395 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8396 unsetvar(vp->text);
8397 } else {
8398 if (vp->func)
8399 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8400 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8401 free((char*)vp->text);
8402 vp->flags = lvp->flags;
8403 vp->text = lvp->text;
8404 }
8405 free(lvp);
8406 }
8407}
8408
8409static int
8410evalfun(struct funcnode *func, int argc, char **argv, int flags)
8411{
8412 volatile struct shparam saveparam;
8413 struct localvar *volatile savelocalvars;
8414 struct jmploc *volatile savehandler;
8415 struct jmploc jmploc;
8416 int e;
8417
8418 saveparam = shellparam;
8419 savelocalvars = localvars;
8420 e = setjmp(jmploc.loc);
8421 if (e) {
8422 goto funcdone;
8423 }
8424 INT_OFF;
8425 savehandler = exception_handler;
8426 exception_handler = &jmploc;
8427 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008428 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008429 func->count++;
8430 funcnest++;
8431 INT_ON;
8432 shellparam.nparam = argc - 1;
8433 shellparam.p = argv + 1;
8434#if ENABLE_ASH_GETOPTS
8435 shellparam.optind = 1;
8436 shellparam.optoff = -1;
8437#endif
8438 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008439 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008440 INT_OFF;
8441 funcnest--;
8442 freefunc(func);
8443 poplocalvars();
8444 localvars = savelocalvars;
8445 freeparam(&shellparam);
8446 shellparam = saveparam;
8447 exception_handler = savehandler;
8448 INT_ON;
8449 evalskip &= ~SKIPFUNC;
8450 return e;
8451}
8452
Denis Vlasenko131ae172007-02-18 13:00:19 +00008453#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008454static char **
8455parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008456{
8457 char *cp, c;
8458
8459 for (;;) {
8460 cp = *++argv;
8461 if (!cp)
8462 return 0;
8463 if (*cp++ != '-')
8464 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008465 c = *cp++;
8466 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008467 break;
8468 if (c == '-' && !*cp) {
8469 argv++;
8470 break;
8471 }
8472 do {
8473 switch (c) {
8474 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008475 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008476 break;
8477 default:
8478 /* run 'typecmd' for other options */
8479 return 0;
8480 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008481 c = *cp++;
8482 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008483 }
8484 return argv;
8485}
8486#endif
8487
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008488/*
8489 * Make a variable a local variable. When a variable is made local, it's
8490 * value and flags are saved in a localvar structure. The saved values
8491 * will be restored when the shell function returns. We handle the name
8492 * "-" as a special case.
8493 */
8494static void
8495mklocal(char *name)
8496{
8497 struct localvar *lvp;
8498 struct var **vpp;
8499 struct var *vp;
8500
8501 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008502 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008503 if (LONE_DASH(name)) {
8504 char *p;
8505 p = ckmalloc(sizeof(optlist));
8506 lvp->text = memcpy(p, optlist, sizeof(optlist));
8507 vp = NULL;
8508 } else {
8509 char *eq;
8510
8511 vpp = hashvar(name);
8512 vp = *findvar(vpp, name);
8513 eq = strchr(name, '=');
8514 if (vp == NULL) {
8515 if (eq)
8516 setvareq(name, VSTRFIXED);
8517 else
8518 setvar(name, NULL, VSTRFIXED);
8519 vp = *vpp; /* the new variable */
8520 lvp->flags = VUNSET;
8521 } else {
8522 lvp->text = vp->text;
8523 lvp->flags = vp->flags;
8524 vp->flags |= VSTRFIXED|VTEXTFIXED;
8525 if (eq)
8526 setvareq(name, 0);
8527 }
8528 }
8529 lvp->vp = vp;
8530 lvp->next = localvars;
8531 localvars = lvp;
8532 INT_ON;
8533}
8534
8535/*
8536 * The "local" command.
8537 */
8538static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008539localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008540{
8541 char *name;
8542
8543 argv = argptr;
8544 while ((name = *argv++) != NULL) {
8545 mklocal(name);
8546 }
8547 return 0;
8548}
8549
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008550static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008551falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008552{
8553 return 1;
8554}
8555
8556static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008557truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008558{
8559 return 0;
8560}
8561
8562static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008563execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008564{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008565 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008566 iflag = 0; /* exit on error */
8567 mflag = 0;
8568 optschanged();
8569 shellexec(argv + 1, pathval(), 0);
8570 }
8571 return 0;
8572}
8573
8574/*
8575 * The return command.
8576 */
8577static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008578returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008579{
8580 /*
8581 * If called outside a function, do what ksh does;
8582 * skip the rest of the file.
8583 */
8584 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8585 return argv[1] ? number(argv[1]) : exitstatus;
8586}
8587
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008588/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008589static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008590static int dotcmd(int, char **);
8591static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008592static int exitcmd(int, char **);
8593static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008594#if ENABLE_ASH_GETOPTS
8595static int getoptscmd(int, char **);
8596#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008597#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008598static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008599#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008600#if ENABLE_ASH_MATH_SUPPORT
8601static int letcmd(int, char **);
8602#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008603static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008604static int setcmd(int, char **);
8605static int shiftcmd(int, char **);
8606static int timescmd(int, char **);
8607static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008608static int umaskcmd(int, char **);
8609static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008610static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008611
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008612#define BUILTIN_NOSPEC "0"
8613#define BUILTIN_SPECIAL "1"
8614#define BUILTIN_REGULAR "2"
8615#define BUILTIN_SPEC_REG "3"
8616#define BUILTIN_ASSIGN "4"
8617#define BUILTIN_SPEC_ASSG "5"
8618#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008619#define BUILTIN_SPEC_REG_ASSG "7"
8620
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008621/* We do not handle [[ expr ]] bashism bash-compatibly,
8622 * we make it a synonym of [ expr ].
8623 * Basically, word splitting and pathname expansion should NOT be performed
8624 * Examples:
8625 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8626 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8627 * Additional operators:
8628 * || and && should work as -o and -a
8629 * =~ regexp match
8630 * Apart from the above, [[ expr ]] should work as [ expr ]
8631 */
8632
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008633#define echocmd echo_main
8634#define printfcmd printf_main
8635#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008636
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008637/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008638static const struct builtincmd builtintab[] = {
8639 { BUILTIN_SPEC_REG ".", dotcmd },
8640 { BUILTIN_SPEC_REG ":", truecmd },
8641#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008642 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008643#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008644 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008645#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008646#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008647#if ENABLE_ASH_ALIAS
8648 { BUILTIN_REG_ASSG "alias", aliascmd },
8649#endif
8650#if JOBS
8651 { BUILTIN_REGULAR "bg", fg_bgcmd },
8652#endif
8653 { BUILTIN_SPEC_REG "break", breakcmd },
8654 { BUILTIN_REGULAR "cd", cdcmd },
8655 { BUILTIN_NOSPEC "chdir", cdcmd },
8656#if ENABLE_ASH_CMDCMD
8657 { BUILTIN_REGULAR "command", commandcmd },
8658#endif
8659 { BUILTIN_SPEC_REG "continue", breakcmd },
8660#if ENABLE_ASH_BUILTIN_ECHO
8661 { BUILTIN_REGULAR "echo", echocmd },
8662#endif
8663 { BUILTIN_SPEC_REG "eval", evalcmd },
8664 { BUILTIN_SPEC_REG "exec", execcmd },
8665 { BUILTIN_SPEC_REG "exit", exitcmd },
8666 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8667 { BUILTIN_REGULAR "false", falsecmd },
8668#if JOBS
8669 { BUILTIN_REGULAR "fg", fg_bgcmd },
8670#endif
8671#if ENABLE_ASH_GETOPTS
8672 { BUILTIN_REGULAR "getopts", getoptscmd },
8673#endif
8674 { BUILTIN_NOSPEC "hash", hashcmd },
8675#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8676 { BUILTIN_NOSPEC "help", helpcmd },
8677#endif
8678#if JOBS
8679 { BUILTIN_REGULAR "jobs", jobscmd },
8680 { BUILTIN_REGULAR "kill", killcmd },
8681#endif
8682#if ENABLE_ASH_MATH_SUPPORT
8683 { BUILTIN_NOSPEC "let", letcmd },
8684#endif
8685 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008686#if ENABLE_ASH_BUILTIN_PRINTF
8687 { BUILTIN_REGULAR "printf", printfcmd },
8688#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008689 { BUILTIN_NOSPEC "pwd", pwdcmd },
8690 { BUILTIN_REGULAR "read", readcmd },
8691 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8692 { BUILTIN_SPEC_REG "return", returncmd },
8693 { BUILTIN_SPEC_REG "set", setcmd },
8694 { BUILTIN_SPEC_REG "shift", shiftcmd },
8695 { BUILTIN_SPEC_REG "source", dotcmd },
8696#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008697 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008698#endif
8699 { BUILTIN_SPEC_REG "times", timescmd },
8700 { BUILTIN_SPEC_REG "trap", trapcmd },
8701 { BUILTIN_REGULAR "true", truecmd },
8702 { BUILTIN_NOSPEC "type", typecmd },
8703 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8704 { BUILTIN_REGULAR "umask", umaskcmd },
8705#if ENABLE_ASH_ALIAS
8706 { BUILTIN_REGULAR "unalias", unaliascmd },
8707#endif
8708 { BUILTIN_SPEC_REG "unset", unsetcmd },
8709 { BUILTIN_REGULAR "wait", waitcmd },
8710};
8711
Denis Vlasenko80591b02008-03-25 07:49:43 +00008712/* Should match the above table! */
8713#define COMMANDCMD (builtintab + \
8714 2 + \
8715 1 * ENABLE_ASH_BUILTIN_TEST + \
8716 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8717 1 * ENABLE_ASH_ALIAS + \
8718 1 * ENABLE_ASH_JOB_CONTROL + \
8719 3)
8720#define EXECCMD (builtintab + \
8721 2 + \
8722 1 * ENABLE_ASH_BUILTIN_TEST + \
8723 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8724 1 * ENABLE_ASH_ALIAS + \
8725 1 * ENABLE_ASH_JOB_CONTROL + \
8726 3 + \
8727 1 * ENABLE_ASH_CMDCMD + \
8728 1 + \
8729 ENABLE_ASH_BUILTIN_ECHO + \
8730 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008731
8732/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008733 * Search the table of builtin commands.
8734 */
8735static struct builtincmd *
8736find_builtin(const char *name)
8737{
8738 struct builtincmd *bp;
8739
8740 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008741 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008742 pstrcmp
8743 );
8744 return bp;
8745}
8746
8747/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008748 * Execute a simple command.
8749 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008750static int
8751isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008752{
8753 const char *q = endofname(p);
8754 if (p == q)
8755 return 0;
8756 return *q == '=';
8757}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008758static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008759bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008760{
8761 /* Preserve exitstatus of a previous possible redirection
8762 * as POSIX mandates */
8763 return back_exitstatus;
8764}
Eric Andersenc470f442003-07-28 09:56:35 +00008765static void
8766evalcommand(union node *cmd, int flags)
8767{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008768 static const struct builtincmd null_bltin = {
8769 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008770 };
Eric Andersenc470f442003-07-28 09:56:35 +00008771 struct stackmark smark;
8772 union node *argp;
8773 struct arglist arglist;
8774 struct arglist varlist;
8775 char **argv;
8776 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008777 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008778 struct cmdentry cmdentry;
8779 struct job *jp;
8780 char *lastarg;
8781 const char *path;
8782 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008783 int status;
8784 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008785 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008786 smallint cmd_is_exec;
8787 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008788
8789 /* First expand the arguments. */
8790 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8791 setstackmark(&smark);
8792 back_exitstatus = 0;
8793
8794 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008795 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008796 varlist.lastp = &varlist.list;
8797 *varlist.lastp = NULL;
8798 arglist.lastp = &arglist.list;
8799 *arglist.lastp = NULL;
8800
8801 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008802 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008803 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8804 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8805 }
8806
Eric Andersenc470f442003-07-28 09:56:35 +00008807 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8808 struct strlist **spp;
8809
8810 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008811 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008812 expandarg(argp, &arglist, EXP_VARTILDE);
8813 else
8814 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8815
Eric Andersenc470f442003-07-28 09:56:35 +00008816 for (sp = *spp; sp; sp = sp->next)
8817 argc++;
8818 }
8819
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008820 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008821 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008822 TRACE(("evalcommand arg: %s\n", sp->text));
8823 *nargv++ = sp->text;
8824 }
8825 *nargv = NULL;
8826
8827 lastarg = NULL;
8828 if (iflag && funcnest == 0 && argc > 0)
8829 lastarg = nargv[-1];
8830
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008831 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008832 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008833 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008834
8835 path = vpath.text;
8836 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8837 struct strlist **spp;
8838 char *p;
8839
8840 spp = varlist.lastp;
8841 expandarg(argp, &varlist, EXP_VARTILDE);
8842
8843 /*
8844 * Modify the command lookup path, if a PATH= assignment
8845 * is present
8846 */
8847 p = (*spp)->text;
8848 if (varequal(p, path))
8849 path = p;
8850 }
8851
8852 /* Print the command if xflag is set. */
8853 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008854 int n;
8855 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008856
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008857 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008858 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008859
8860 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008861 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008862 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008863 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008864 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008865 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008866 p--;
8867 }
8868 }
8869 sp = arglist.list;
8870 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008871 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008872 }
8873
8874 cmd_is_exec = 0;
8875 spclbltin = -1;
8876
8877 /* Now locate the command. */
8878 if (argc) {
8879 const char *oldpath;
8880 int cmd_flag = DO_ERR;
8881
8882 path += 5;
8883 oldpath = path;
8884 for (;;) {
8885 find_command(argv[0], &cmdentry, cmd_flag, path);
8886 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008887 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008888 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008889 goto bail;
8890 }
8891
8892 /* implement bltin and command here */
8893 if (cmdentry.cmdtype != CMDBUILTIN)
8894 break;
8895 if (spclbltin < 0)
8896 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8897 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008898 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008899#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008900 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008901 path = oldpath;
8902 nargv = parse_command_args(argv, &path);
8903 if (!nargv)
8904 break;
8905 argc -= nargv - argv;
8906 argv = nargv;
8907 cmd_flag |= DO_NOFUNC;
8908 } else
8909#endif
8910 break;
8911 }
8912 }
8913
8914 if (status) {
8915 /* We have a redirection error. */
8916 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008917 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008918 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008919 exitstatus = status;
8920 goto out;
8921 }
8922
8923 /* Execute the command. */
8924 switch (cmdentry.cmdtype) {
8925 default:
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008926#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008927 {
8928 /* find_command() encodes applet_no as (-2 - applet_no) */
8929 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008930 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008931 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008932 /* run <applet>_main() */
8933 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008934 break;
8935 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008936 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00008937#endif
8938
Eric Andersenc470f442003-07-28 09:56:35 +00008939 /* Fork off a child process if necessary. */
8940 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008941 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008942 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008943 if (forkshell(jp, cmd, FORK_FG) != 0) {
8944 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008945 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008946 break;
8947 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008948 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008949 }
8950 listsetvar(varlist.list, VEXPORT|VSTACK);
8951 shellexec(argv, path, cmdentry.u.index);
8952 /* NOTREACHED */
8953
8954 case CMDBUILTIN:
8955 cmdenviron = varlist.list;
8956 if (cmdenviron) {
8957 struct strlist *list = cmdenviron;
8958 int i = VNOSET;
8959 if (spclbltin > 0 || argc == 0) {
8960 i = 0;
8961 if (cmd_is_exec && argc > 1)
8962 i = VEXPORT;
8963 }
8964 listsetvar(list, i);
8965 }
8966 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8967 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008968 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008969 if (i == EXEXIT)
8970 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008971 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008972 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008973 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008974 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008975 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008976 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008977 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008978 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008979 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008980 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008981 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008982 }
8983 break;
8984
8985 case CMDFUNCTION:
8986 listsetvar(varlist.list, 0);
8987 if (evalfun(cmdentry.u.func, argc, argv, flags))
8988 goto raise;
8989 break;
8990 }
8991
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008992 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008993 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008994 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00008995 /* dsl: I think this is intended to be used to support
8996 * '_' in 'vi' command mode during line editing...
8997 * However I implemented that within libedit itself.
8998 */
8999 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009000 }
Eric Andersenc470f442003-07-28 09:56:35 +00009001 popstackmark(&smark);
9002}
9003
9004static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009005evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9006{
Eric Andersenc470f442003-07-28 09:56:35 +00009007 char *volatile savecmdname;
9008 struct jmploc *volatile savehandler;
9009 struct jmploc jmploc;
9010 int i;
9011
9012 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009013 i = setjmp(jmploc.loc);
9014 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009015 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009016 savehandler = exception_handler;
9017 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009018 commandname = argv[0];
9019 argptr = argv + 1;
9020 optptr = NULL; /* initialize nextopt */
9021 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009022 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009023 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009024 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009025 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009026 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009027// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009028 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009029
9030 return i;
9031}
9032
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009033static int
9034goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009035{
9036 return !*endofname(p);
9037}
9038
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009039
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009040/*
9041 * Search for a command. This is called before we fork so that the
9042 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009043 * the child. The check for "goodname" is an overly conservative
9044 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009045 */
Eric Andersenc470f442003-07-28 09:56:35 +00009046static void
9047prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009048{
9049 struct cmdentry entry;
9050
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009051 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9052 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009053}
9054
Eric Andersencb57d552001-06-28 07:25:16 +00009055
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009056/* ============ Builtin commands
9057 *
9058 * Builtin commands whose functions are closely tied to evaluation
9059 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009060 */
9061
9062/*
Eric Andersencb57d552001-06-28 07:25:16 +00009063 * Handle break and continue commands. Break, continue, and return are
9064 * all handled by setting the evalskip flag. The evaluation routines
9065 * above all check this flag, and if it is set they start skipping
9066 * commands rather than executing them. The variable skipcount is
9067 * the number of loops to break/continue, or the number of function
9068 * levels to return. (The latter is always 1.) It should probably
9069 * be an error to break out of more loops than exist, but it isn't
9070 * in the standard shell so we don't make it one here.
9071 */
Eric Andersenc470f442003-07-28 09:56:35 +00009072static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009073breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009074{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009075 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009076
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009077 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009078 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009079 if (n > loopnest)
9080 n = loopnest;
9081 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009082 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009083 skipcount = n;
9084 }
9085 return 0;
9086}
9087
Eric Andersenc470f442003-07-28 09:56:35 +00009088
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009089/* ============ input.c
9090 *
Eric Andersen90898442003-08-06 11:20:52 +00009091 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009092 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009093
Eric Andersenc470f442003-07-28 09:56:35 +00009094#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00009095
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009096enum {
9097 INPUT_PUSH_FILE = 1,
9098 INPUT_NOFILE_OK = 2,
9099};
Eric Andersencb57d552001-06-28 07:25:16 +00009100
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009101static int plinno = 1; /* input line number */
9102/* number of characters left in input buffer */
9103static int parsenleft; /* copy of parsefile->nleft */
9104static int parselleft; /* copy of parsefile->lleft */
9105/* next character in input buffer */
9106static char *parsenextc; /* copy of parsefile->nextc */
9107
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009108static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009109/* values of checkkwd variable */
9110#define CHKALIAS 0x1
9111#define CHKKWD 0x2
9112#define CHKNL 0x4
9113
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009114static void
9115popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009116{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009117 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009118
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009119 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009120#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009121 if (sp->ap) {
9122 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
9123 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009124 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009125 if (sp->string != sp->ap->val) {
9126 free(sp->string);
9127 }
9128 sp->ap->flag &= ~ALIASINUSE;
9129 if (sp->ap->flag & ALIASDEAD) {
9130 unalias(sp->ap->name);
9131 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009132 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009133#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009134 parsenextc = sp->prevstring;
9135 parsenleft = sp->prevnleft;
9136/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009137 g_parsefile->strpush = sp->prev;
9138 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009139 free(sp);
9140 INT_ON;
9141}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009142
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009143static int
9144preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009145{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009146 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009147 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009148 parsenextc = buf;
9149
Denis Vlasenko38f63192007-01-22 09:03:07 +00009150#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009151 retry:
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009152 if (!iflag || g_parsefile->fd)
9153 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009154 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009155#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009156 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009157#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009158 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9159 if (nr == 0) {
9160 /* Ctrl+C pressed */
9161 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009162 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009163 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009164 raise(SIGINT);
9165 return 1;
9166 }
Eric Andersenc470f442003-07-28 09:56:35 +00009167 goto retry;
9168 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009169 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009170 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009171 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009172 }
Eric Andersencb57d552001-06-28 07:25:16 +00009173 }
9174#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009175 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009176#endif
9177
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009178#if 0
9179/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009180 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009181 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009182 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009183 if (flags >= 0 && (flags & O_NONBLOCK)) {
9184 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009185 if (fcntl(0, F_SETFL, flags) >= 0) {
9186 out2str("sh: turning off NDELAY mode\n");
9187 goto retry;
9188 }
9189 }
9190 }
9191 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009192#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009193 return nr;
9194}
9195
9196/*
9197 * Refill the input buffer and return the next input character:
9198 *
9199 * 1) If a string was pushed back on the input, pop it;
9200 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9201 * from a string so we can't refill the buffer, return EOF.
9202 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9203 * 4) Process input up to the next newline, deleting nul characters.
9204 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009205static int
Eric Andersenc470f442003-07-28 09:56:35 +00009206preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009207{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009208 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009209 int more;
9210 char savec;
9211
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009212 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009213#if ENABLE_ASH_ALIAS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009214 if (parsenleft == -1 && g_parsefile->strpush->ap &&
Eric Andersen2870d962001-07-02 17:27:21 +00009215 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009216 return PEOA;
9217 }
Eric Andersen2870d962001-07-02 17:27:21 +00009218#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009219 popstring();
9220 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009221 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009222 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009223 if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009224 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009225 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009226
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009227 more = parselleft;
9228 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009229 again:
9230 more = preadfd();
9231 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009232 parselleft = parsenleft = EOF_NLEFT;
9233 return PEOF;
9234 }
9235 }
9236
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009237 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009238
9239 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009240 for (;;) {
9241 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009242
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009243 more--;
9244 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009245
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009246 if (!c)
9247 memmove(q, q + 1, more);
9248 else {
9249 q++;
9250 if (c == '\n') {
9251 parsenleft = q - parsenextc - 1;
9252 break;
9253 }
Eric Andersencb57d552001-06-28 07:25:16 +00009254 }
9255
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009256 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009257 parsenleft = q - parsenextc - 1;
9258 if (parsenleft < 0)
9259 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009260 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009261 }
9262 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009263 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009264
9265 savec = *q;
9266 *q = '\0';
9267
9268 if (vflag) {
9269 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009270 }
9271
9272 *q = savec;
9273
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009274 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009275}
9276
Denis Vlasenko834dee72008-10-07 09:18:30 +00009277#define pgetc_as_macro() (--parsenleft >= 0 ? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009278static int
9279pgetc(void)
9280{
9281 return pgetc_as_macro();
9282}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009283
9284#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009285#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009286#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009287#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009288#endif
9289
9290/*
9291 * Same as pgetc(), but ignores PEOA.
9292 */
9293#if ENABLE_ASH_ALIAS
9294static int
9295pgetc2(void)
9296{
9297 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009298 do {
Denis Vlasenko834dee72008-10-07 09:18:30 +00009299 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009300 } while (c == PEOA);
9301 return c;
9302}
9303#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009304#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009305#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;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009358 if (g_parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009359 sp = ckzalloc(sizeof(struct strpush));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009360 sp->prev = g_parsefile->strpush;
9361 g_parsefile->strpush = sp;
Eric Andersencb57d552001-06-28 07:25:16 +00009362 } else
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009363 sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
Eric Andersencb57d552001-06-28 07:25:16 +00009364 sp->prevstring = parsenextc;
9365 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009366#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009367 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009368 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009369 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009370 sp->string = s;
9371 }
Eric Andersen2870d962001-07-02 17:27:21 +00009372#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009373 parsenextc = s;
9374 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009375 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009376}
9377
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009378/*
9379 * To handle the "." command, a stack of input files is used. Pushfile
9380 * adds a new entry to the stack and popfile restores the previous level.
9381 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009382static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009383pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009384{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009385 struct parsefile *pf;
9386
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009387 g_parsefile->nleft = parsenleft;
9388 g_parsefile->lleft = parselleft;
9389 g_parsefile->nextc = parsenextc;
9390 g_parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009391 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009392 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009393 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009394 /*pf->strpush = NULL; - ckzalloc did it */
9395 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009396 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009397}
9398
9399static void
9400popfile(void)
9401{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009402 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009403
Denis Vlasenkob012b102007-02-19 22:43:01 +00009404 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009405 if (pf->fd >= 0)
9406 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009407 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009408 while (pf->strpush)
9409 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009410 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009411 free(pf);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009412 parsenleft = g_parsefile->nleft;
9413 parselleft = g_parsefile->lleft;
9414 parsenextc = g_parsefile->nextc;
9415 plinno = g_parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009416 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009417}
9418
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009419/*
9420 * Return to top level.
9421 */
9422static void
9423popallfiles(void)
9424{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009425 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009426 popfile();
9427}
9428
9429/*
9430 * Close the file(s) that the shell is reading commands from. Called
9431 * after a fork is done.
9432 */
9433static void
9434closescript(void)
9435{
9436 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009437 if (g_parsefile->fd > 0) {
9438 close(g_parsefile->fd);
9439 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009440 }
9441}
9442
9443/*
9444 * Like setinputfile, but takes an open file descriptor. Call this with
9445 * interrupts off.
9446 */
9447static void
9448setinputfd(int fd, int push)
9449{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009450 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009451 if (push) {
9452 pushfile();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009453 g_parsefile->buf = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009454 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009455 g_parsefile->fd = fd;
9456 if (g_parsefile->buf == NULL)
9457 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009458 parselleft = parsenleft = 0;
9459 plinno = 1;
9460}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009461
Eric Andersenc470f442003-07-28 09:56:35 +00009462/*
9463 * Set the input to take input from a file. If push is set, push the
9464 * old input onto the stack first.
9465 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009466static int
9467setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009468{
9469 int fd;
9470 int fd2;
9471
Denis Vlasenkob012b102007-02-19 22:43:01 +00009472 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009473 fd = open(fname, O_RDONLY);
9474 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009475 if (flags & INPUT_NOFILE_OK)
9476 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009477 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009478 }
Eric Andersenc470f442003-07-28 09:56:35 +00009479 if (fd < 10) {
9480 fd2 = copyfd(fd, 10);
9481 close(fd);
9482 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009483 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009484 fd = fd2;
9485 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009486 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009487 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009488 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009489 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009490}
9491
Eric Andersencb57d552001-06-28 07:25:16 +00009492/*
9493 * Like setinputfile, but takes input from a string.
9494 */
Eric Andersenc470f442003-07-28 09:56:35 +00009495static void
9496setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009497{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009498 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009499 pushfile();
9500 parsenextc = string;
9501 parsenleft = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009502 g_parsefile->buf = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009503 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009504 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009505}
9506
9507
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009508/* ============ mail.c
9509 *
9510 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009511 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009512
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009513#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009514
Eric Andersencb57d552001-06-28 07:25:16 +00009515#define MAXMBOXES 10
9516
Eric Andersenc470f442003-07-28 09:56:35 +00009517/* times of mailboxes */
9518static time_t mailtime[MAXMBOXES];
9519/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009520static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009521
Eric Andersencb57d552001-06-28 07:25:16 +00009522/*
Eric Andersenc470f442003-07-28 09:56:35 +00009523 * Print appropriate message(s) if mail has arrived.
9524 * If mail_var_path_changed is set,
9525 * then the value of MAIL has mail_var_path_changed,
9526 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009527 */
Eric Andersenc470f442003-07-28 09:56:35 +00009528static void
9529chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009530{
Eric Andersencb57d552001-06-28 07:25:16 +00009531 const char *mpath;
9532 char *p;
9533 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009534 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009535 struct stackmark smark;
9536 struct stat statb;
9537
Eric Andersencb57d552001-06-28 07:25:16 +00009538 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009539 mpath = mpathset() ? mpathval() : mailval();
9540 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009541 p = padvance(&mpath, nullstr);
9542 if (p == NULL)
9543 break;
9544 if (*p == '\0')
9545 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009546 for (q = p; *q; q++)
9547 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009548#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009549 if (q[-1] != '/')
9550 abort();
9551#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009552 q[-1] = '\0'; /* delete trailing '/' */
9553 if (stat(p, &statb) < 0) {
9554 *mtp = 0;
9555 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009556 }
Eric Andersenc470f442003-07-28 09:56:35 +00009557 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9558 fprintf(
9559 stderr, snlfmt,
9560 pathopt ? pathopt : "you have mail"
9561 );
9562 }
9563 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009564 }
Eric Andersenc470f442003-07-28 09:56:35 +00009565 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009566 popstackmark(&smark);
9567}
Eric Andersencb57d552001-06-28 07:25:16 +00009568
Eric Andersenc470f442003-07-28 09:56:35 +00009569static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009570changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009571{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009572 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009573}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009574
Denis Vlasenko131ae172007-02-18 13:00:19 +00009575#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009576
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009577
9578/* ============ ??? */
9579
Eric Andersencb57d552001-06-28 07:25:16 +00009580/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009581 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009582 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009583static void
9584setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009585{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009586 char **newparam;
9587 char **ap;
9588 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009589
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009590 for (nparam = 0; argv[nparam]; nparam++)
9591 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009592 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9593 while (*argv) {
9594 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009595 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009596 *ap = NULL;
9597 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009598 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009599 shellparam.nparam = nparam;
9600 shellparam.p = newparam;
9601#if ENABLE_ASH_GETOPTS
9602 shellparam.optind = 1;
9603 shellparam.optoff = -1;
9604#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009605}
9606
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009607/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009608 * Process shell options. The global variable argptr contains a pointer
9609 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009610 *
9611 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9612 * For a non-interactive shell, an error condition encountered
9613 * by a special built-in ... shall cause the shell to write a diagnostic message
9614 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009615 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009616 * ...
9617 * Utility syntax error (option or operand error) Shall exit
9618 * ...
9619 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9620 * we see that bash does not do that (set "finishes" with error code 1 instead,
9621 * and shell continues), and people rely on this behavior!
9622 * Testcase:
9623 * set -o barfoo 2>/dev/null
9624 * echo $?
9625 *
9626 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009627 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009628static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009629plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009630{
9631 int i;
9632
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009633 if (name) {
9634 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009635 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009636 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009637 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009638 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009639 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009640 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009641 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009642 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009643 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009644 if (val) {
9645 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9646 } else {
9647 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9648 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009649 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009650 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009651}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009652static void
9653setoption(int flag, int val)
9654{
9655 int i;
9656
9657 for (i = 0; i < NOPTS; i++) {
9658 if (optletters(i) == flag) {
9659 optlist[i] = val;
9660 return;
9661 }
9662 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009663 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009664 /* NOTREACHED */
9665}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009666static int
Eric Andersenc470f442003-07-28 09:56:35 +00009667options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009668{
9669 char *p;
9670 int val;
9671 int c;
9672
9673 if (cmdline)
9674 minusc = NULL;
9675 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009676 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009677 if (c != '-' && c != '+')
9678 break;
9679 argptr++;
9680 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009681 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009682 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009683 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009684 if (!cmdline) {
9685 /* "-" means turn off -x and -v */
9686 if (p[0] == '\0')
9687 xflag = vflag = 0;
9688 /* "--" means reset params */
9689 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009690 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009691 }
Eric Andersenc470f442003-07-28 09:56:35 +00009692 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009693 }
Eric Andersencb57d552001-06-28 07:25:16 +00009694 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009695 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009696 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009697 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009698 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009699 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009700 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009701 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009702 /* it already printed err message */
9703 return 1; /* error */
9704 }
Eric Andersencb57d552001-06-28 07:25:16 +00009705 if (*argptr)
9706 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009707 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9708 isloginsh = 1;
9709 /* bash does not accept +-login, we also won't */
9710 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009711 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009712 isloginsh = 1;
9713 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009714 } else {
9715 setoption(c, val);
9716 }
9717 }
9718 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009719 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009720}
9721
Eric Andersencb57d552001-06-28 07:25:16 +00009722/*
Eric Andersencb57d552001-06-28 07:25:16 +00009723 * The shift builtin command.
9724 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009725static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009726shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009727{
9728 int n;
9729 char **ap1, **ap2;
9730
9731 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009732 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009733 n = number(argv[1]);
9734 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009735 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009736 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009737 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009738 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009739 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009740 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009741 }
9742 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009743 while ((*ap2++ = *ap1++) != NULL)
9744 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009745#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009746 shellparam.optind = 1;
9747 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009748#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009749 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009750 return 0;
9751}
9752
Eric Andersencb57d552001-06-28 07:25:16 +00009753/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009754 * POSIX requires that 'set' (but not export or readonly) output the
9755 * variables in lexicographic order - by the locale's collating order (sigh).
9756 * Maybe we could keep them in an ordered balanced binary tree
9757 * instead of hashed lists.
9758 * For now just roll 'em through qsort for printing...
9759 */
9760static int
9761showvars(const char *sep_prefix, int on, int off)
9762{
9763 const char *sep;
9764 char **ep, **epend;
9765
9766 ep = listvars(on, off, &epend);
9767 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9768
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009769 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009770
9771 for (; ep < epend; ep++) {
9772 const char *p;
9773 const char *q;
9774
9775 p = strchrnul(*ep, '=');
9776 q = nullstr;
9777 if (*p)
9778 q = single_quote(++p);
9779 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9780 }
9781 return 0;
9782}
9783
9784/*
Eric Andersencb57d552001-06-28 07:25:16 +00009785 * The set command builtin.
9786 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009787static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009788setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009789{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009790 int retval;
9791
Denis Vlasenko68404f12008-03-17 09:00:54 +00009792 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009793 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009794 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009795 retval = 1;
9796 if (!options(0)) { /* if no parse error... */
9797 retval = 0;
9798 optschanged();
9799 if (*argptr != NULL) {
9800 setparam(argptr);
9801 }
Eric Andersencb57d552001-06-28 07:25:16 +00009802 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009803 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009804 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009805}
9806
Denis Vlasenko131ae172007-02-18 13:00:19 +00009807#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009808static void
9809change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009810{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009811 /* Galois LFSR parameter */
9812 /* Taps at 32 31 29 1: */
9813 enum { MASK = 0x8000000b };
9814 /* Another example - taps at 32 31 30 10: */
9815 /* MASK = 0x00400007 */
9816
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009817 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009818 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009819 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009820
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009821 /* LCG has period of 2^32 and alternating lowest bit */
9822 random_LCG = 1664525 * random_LCG + 1013904223;
9823 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9824 t = (random_galois_LFSR << 1);
9825 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9826 t ^= MASK;
9827 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009828 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009829 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009830 * for $RANDOM range. Combining with subtraction is
9831 * just for fun. + and ^ would work equally well. */
9832 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009833 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009834 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009835 vrandom.flags &= ~VNOFUNC;
9836 } else {
9837 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009838 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009839 }
Eric Andersenef02f822004-03-11 13:34:24 +00009840}
Eric Andersen16767e22004-03-16 05:14:10 +00009841#endif
9842
Denis Vlasenko131ae172007-02-18 13:00:19 +00009843#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009844static int
Eric Andersenc470f442003-07-28 09:56:35 +00009845getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009846{
9847 char *p, *q;
9848 char c = '?';
9849 int done = 0;
9850 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009851 char s[12];
9852 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009853
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009854 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009855 return 1;
9856 optnext = optfirst + *param_optind - 1;
9857
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009858 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009859 p = NULL;
9860 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009861 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009862 if (p == NULL || *p == '\0') {
9863 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009864 p = *optnext;
9865 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009866 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009867 p = NULL;
9868 done = 1;
9869 goto out;
9870 }
9871 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009872 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009873 goto atend;
9874 }
9875
9876 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009877 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009878 if (*q == '\0') {
9879 if (optstr[0] == ':') {
9880 s[0] = c;
9881 s[1] = '\0';
9882 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009883 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009884 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009885 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009886 }
9887 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009888 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009889 }
9890 if (*++q == ':')
9891 q++;
9892 }
9893
9894 if (*++q == ':') {
9895 if (*p == '\0' && (p = *optnext) == NULL) {
9896 if (optstr[0] == ':') {
9897 s[0] = c;
9898 s[1] = '\0';
9899 err |= setvarsafe("OPTARG", s, 0);
9900 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009901 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009902 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009903 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009904 c = '?';
9905 }
Eric Andersenc470f442003-07-28 09:56:35 +00009906 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009907 }
9908
9909 if (p == *optnext)
9910 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009911 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009912 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009913 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009914 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009915 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009916 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009917 *param_optind = optnext - optfirst + 1;
9918 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009919 err |= setvarsafe("OPTIND", s, VNOFUNC);
9920 s[0] = c;
9921 s[1] = '\0';
9922 err |= setvarsafe(optvar, s, 0);
9923 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009924 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009925 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009926 flush_stdout_stderr();
9927 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009928 }
9929 return done;
9930}
Eric Andersenc470f442003-07-28 09:56:35 +00009931
9932/*
9933 * The getopts builtin. Shellparam.optnext points to the next argument
9934 * to be processed. Shellparam.optptr points to the next character to
9935 * be processed in the current argument. If shellparam.optnext is NULL,
9936 * then it's the first time getopts has been called.
9937 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009938static int
Eric Andersenc470f442003-07-28 09:56:35 +00009939getoptscmd(int argc, char **argv)
9940{
9941 char **optbase;
9942
9943 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009944 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009945 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009946 optbase = shellparam.p;
9947 if (shellparam.optind > shellparam.nparam + 1) {
9948 shellparam.optind = 1;
9949 shellparam.optoff = -1;
9950 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009951 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009952 optbase = &argv[3];
9953 if (shellparam.optind > argc - 2) {
9954 shellparam.optind = 1;
9955 shellparam.optoff = -1;
9956 }
9957 }
9958
9959 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009960 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009961}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009962#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009963
Eric Andersencb57d552001-06-28 07:25:16 +00009964
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009965/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009966
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009967struct heredoc {
9968 struct heredoc *next; /* next here document in list */
9969 union node *here; /* redirection node */
9970 char *eofmark; /* string indicating end of input */
9971 smallint striptabs; /* if set, strip leading tabs */
9972};
9973
9974static smallint tokpushback; /* last token pushed back */
9975static smallint parsebackquote; /* nonzero if we are inside backquotes */
9976static smallint quoteflag; /* set if (part of) last token was quoted */
9977static token_id_t lasttoken; /* last token read (integer id Txxx) */
9978static struct heredoc *heredoclist; /* list of here documents to read */
9979static char *wordtext; /* text of last word returned by readtoken */
9980static struct nodelist *backquotelist;
9981static union node *redirnode;
9982static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009983/*
9984 * NEOF is returned by parsecmd when it encounters an end of file. It
9985 * must be distinct from NULL, so we use the address of a variable that
9986 * happens to be handy.
9987 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009988#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009989
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009990static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009991static void
9992raise_error_syntax(const char *msg)
9993{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009994 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009995 /* NOTREACHED */
9996}
9997
9998/*
9999 * Called when an unexpected token is read during the parse. The argument
10000 * is the token that is expected, or -1 if more than one type of token can
10001 * occur at this point.
10002 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010003static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010004static void
10005raise_error_unexpected_syntax(int token)
10006{
10007 char msg[64];
10008 int l;
10009
10010 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10011 if (token >= 0)
10012 sprintf(msg + l, " (expecting %s)", tokname(token));
10013 raise_error_syntax(msg);
10014 /* NOTREACHED */
10015}
Eric Andersencb57d552001-06-28 07:25:16 +000010016
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010017#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010018
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010019/* parsing is heavily cross-recursive, need these forward decls */
10020static union node *andor(void);
10021static union node *pipeline(void);
10022static union node *parse_command(void);
10023static void parseheredoc(void);
10024static char peektoken(void);
10025static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010026
Eric Andersenc470f442003-07-28 09:56:35 +000010027static union node *
10028list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010029{
10030 union node *n1, *n2, *n3;
10031 int tok;
10032
Eric Andersenc470f442003-07-28 09:56:35 +000010033 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10034 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010035 return NULL;
10036 n1 = NULL;
10037 for (;;) {
10038 n2 = andor();
10039 tok = readtoken();
10040 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010041 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010042 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010043 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010044 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010045 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010046 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010047 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010048 n2 = n3;
10049 }
10050 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010051 }
10052 }
10053 if (n1 == NULL) {
10054 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010055 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010056 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010057 n3->type = NSEMI;
10058 n3->nbinary.ch1 = n1;
10059 n3->nbinary.ch2 = n2;
10060 n1 = n3;
10061 }
10062 switch (tok) {
10063 case TBACKGND:
10064 case TSEMI:
10065 tok = readtoken();
10066 /* fall through */
10067 case TNL:
10068 if (tok == TNL) {
10069 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010070 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010071 return n1;
10072 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010073 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010074 }
Eric Andersenc470f442003-07-28 09:56:35 +000010075 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010076 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010077 return n1;
10078 break;
10079 case TEOF:
10080 if (heredoclist)
10081 parseheredoc();
10082 else
Eric Andersenc470f442003-07-28 09:56:35 +000010083 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010084 return n1;
10085 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010086 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010087 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010088 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010089 return n1;
10090 }
10091 }
10092}
10093
Eric Andersenc470f442003-07-28 09:56:35 +000010094static union node *
10095andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010096{
Eric Andersencb57d552001-06-28 07:25:16 +000010097 union node *n1, *n2, *n3;
10098 int t;
10099
Eric Andersencb57d552001-06-28 07:25:16 +000010100 n1 = pipeline();
10101 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010102 t = readtoken();
10103 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010104 t = NAND;
10105 } else if (t == TOR) {
10106 t = NOR;
10107 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010108 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010109 return n1;
10110 }
Eric Andersenc470f442003-07-28 09:56:35 +000010111 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010112 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010113 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010114 n3->type = t;
10115 n3->nbinary.ch1 = n1;
10116 n3->nbinary.ch2 = n2;
10117 n1 = n3;
10118 }
10119}
10120
Eric Andersenc470f442003-07-28 09:56:35 +000010121static union node *
10122pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010123{
Eric Andersencb57d552001-06-28 07:25:16 +000010124 union node *n1, *n2, *pipenode;
10125 struct nodelist *lp, *prev;
10126 int negate;
10127
10128 negate = 0;
10129 TRACE(("pipeline: entered\n"));
10130 if (readtoken() == TNOT) {
10131 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010132 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010133 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010134 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010135 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010136 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010137 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010138 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010139 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010140 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010141 pipenode->npipe.cmdlist = lp;
10142 lp->n = n1;
10143 do {
10144 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010145 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010146 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010147 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010148 prev->next = lp;
10149 } while (readtoken() == TPIPE);
10150 lp->next = NULL;
10151 n1 = pipenode;
10152 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010153 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010154 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010155 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010156 n2->type = NNOT;
10157 n2->nnot.com = n1;
10158 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010159 }
10160 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010161}
10162
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010163static union node *
10164makename(void)
10165{
10166 union node *n;
10167
Denis Vlasenko597906c2008-02-20 16:38:54 +000010168 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010169 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010170 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010171 n->narg.text = wordtext;
10172 n->narg.backquote = backquotelist;
10173 return n;
10174}
10175
10176static void
10177fixredir(union node *n, const char *text, int err)
10178{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010179 int fd;
10180
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010181 TRACE(("Fix redir %s %d\n", text, err));
10182 if (!err)
10183 n->ndup.vname = NULL;
10184
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010185 fd = bb_strtou(text, NULL, 10);
10186 if (!errno && fd >= 0)
10187 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010188 else if (LONE_DASH(text))
10189 n->ndup.dupfd = -1;
10190 else {
10191 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010192 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010193 n->ndup.vname = makename();
10194 }
10195}
10196
10197/*
10198 * Returns true if the text contains nothing to expand (no dollar signs
10199 * or backquotes).
10200 */
10201static int
10202noexpand(char *text)
10203{
10204 char *p;
10205 char c;
10206
10207 p = text;
10208 while ((c = *p++) != '\0') {
10209 if (c == CTLQUOTEMARK)
10210 continue;
10211 if (c == CTLESC)
10212 p++;
10213 else if (SIT(c, BASESYNTAX) == CCTL)
10214 return 0;
10215 }
10216 return 1;
10217}
10218
10219static void
10220parsefname(void)
10221{
10222 union node *n = redirnode;
10223
10224 if (readtoken() != TWORD)
10225 raise_error_unexpected_syntax(-1);
10226 if (n->type == NHERE) {
10227 struct heredoc *here = heredoc;
10228 struct heredoc *p;
10229 int i;
10230
10231 if (quoteflag == 0)
10232 n->type = NXHERE;
10233 TRACE(("Here document %d\n", n->type));
10234 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010235 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010236 rmescapes(wordtext);
10237 here->eofmark = wordtext;
10238 here->next = NULL;
10239 if (heredoclist == NULL)
10240 heredoclist = here;
10241 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010242 for (p = heredoclist; p->next; p = p->next)
10243 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010244 p->next = here;
10245 }
10246 } else if (n->type == NTOFD || n->type == NFROMFD) {
10247 fixredir(n, wordtext, 0);
10248 } else {
10249 n->nfile.fname = makename();
10250 }
10251}
Eric Andersencb57d552001-06-28 07:25:16 +000010252
Eric Andersenc470f442003-07-28 09:56:35 +000010253static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010254simplecmd(void)
10255{
10256 union node *args, **app;
10257 union node *n = NULL;
10258 union node *vars, **vpp;
10259 union node **rpp, *redir;
10260 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010261#if ENABLE_ASH_BASH_COMPAT
10262 smallint double_brackets_flag = 0;
10263#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010264
10265 args = NULL;
10266 app = &args;
10267 vars = NULL;
10268 vpp = &vars;
10269 redir = NULL;
10270 rpp = &redir;
10271
10272 savecheckkwd = CHKALIAS;
10273 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010274 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010275 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010276 t = readtoken();
10277 switch (t) {
10278#if ENABLE_ASH_BASH_COMPAT
10279 case TAND: /* "&&" */
10280 case TOR: /* "||" */
10281 if (!double_brackets_flag) {
10282 tokpushback = 1;
10283 goto out;
10284 }
10285 wordtext = (char *) (t == TAND ? "-a" : "-o");
10286#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010287 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010288 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010289 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010290 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010291 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010292#if ENABLE_ASH_BASH_COMPAT
10293 if (strcmp("[[", wordtext) == 0)
10294 double_brackets_flag = 1;
10295 else if (strcmp("]]", wordtext) == 0)
10296 double_brackets_flag = 0;
10297#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010298 n->narg.backquote = backquotelist;
10299 if (savecheckkwd && isassignment(wordtext)) {
10300 *vpp = n;
10301 vpp = &n->narg.next;
10302 } else {
10303 *app = n;
10304 app = &n->narg.next;
10305 savecheckkwd = 0;
10306 }
10307 break;
10308 case TREDIR:
10309 *rpp = n = redirnode;
10310 rpp = &n->nfile.next;
10311 parsefname(); /* read name of redirection file */
10312 break;
10313 case TLP:
10314 if (args && app == &args->narg.next
10315 && !vars && !redir
10316 ) {
10317 struct builtincmd *bcmd;
10318 const char *name;
10319
10320 /* We have a function */
10321 if (readtoken() != TRP)
10322 raise_error_unexpected_syntax(TRP);
10323 name = n->narg.text;
10324 if (!goodname(name)
10325 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10326 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010327 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010328 }
10329 n->type = NDEFUN;
10330 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10331 n->narg.next = parse_command();
10332 return n;
10333 }
10334 /* fall through */
10335 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010336 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010337 goto out;
10338 }
10339 }
10340 out:
10341 *app = NULL;
10342 *vpp = NULL;
10343 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010344 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010345 n->type = NCMD;
10346 n->ncmd.args = args;
10347 n->ncmd.assign = vars;
10348 n->ncmd.redirect = redir;
10349 return n;
10350}
10351
10352static union node *
10353parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010354{
Eric Andersencb57d552001-06-28 07:25:16 +000010355 union node *n1, *n2;
10356 union node *ap, **app;
10357 union node *cp, **cpp;
10358 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010359 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010360 int t;
10361
10362 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010363 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010364
Eric Andersencb57d552001-06-28 07:25:16 +000010365 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010366 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010367 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010368 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010369 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010370 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010371 n1->type = NIF;
10372 n1->nif.test = list(0);
10373 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010374 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010375 n1->nif.ifpart = list(0);
10376 n2 = n1;
10377 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010378 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010379 n2 = n2->nif.elsepart;
10380 n2->type = NIF;
10381 n2->nif.test = list(0);
10382 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010383 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010384 n2->nif.ifpart = list(0);
10385 }
10386 if (lasttoken == TELSE)
10387 n2->nif.elsepart = list(0);
10388 else {
10389 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010390 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010391 }
Eric Andersenc470f442003-07-28 09:56:35 +000010392 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010393 break;
10394 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010395 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010396 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010397 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010398 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010399 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010400 got = readtoken();
10401 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010402 TRACE(("expecting DO got %s %s\n", tokname(got),
10403 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010404 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010405 }
10406 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010407 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010408 break;
10409 }
10410 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010411 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010412 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010413 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010414 n1->type = NFOR;
10415 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010416 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010417 if (readtoken() == TIN) {
10418 app = &ap;
10419 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010420 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010421 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010422 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010423 n2->narg.text = wordtext;
10424 n2->narg.backquote = backquotelist;
10425 *app = n2;
10426 app = &n2->narg.next;
10427 }
10428 *app = NULL;
10429 n1->nfor.args = ap;
10430 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010431 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010432 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010433 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010434 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010435 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010436 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010437 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010438 n1->nfor.args = n2;
10439 /*
10440 * Newline or semicolon here is optional (but note
10441 * that the original Bourne shell only allowed NL).
10442 */
10443 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010444 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010445 }
Eric Andersenc470f442003-07-28 09:56:35 +000010446 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010447 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010448 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010449 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010450 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010451 break;
10452 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010453 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010454 n1->type = NCASE;
10455 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010456 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010457 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010458 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010459 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010460 n2->narg.text = wordtext;
10461 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010462 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010463 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010464 } while (readtoken() == TNL);
10465 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010466 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010467 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010468 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010469 checkkwd = CHKNL | CHKKWD;
10470 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010471 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010472 if (lasttoken == TLP)
10473 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010474 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010475 cp->type = NCLIST;
10476 app = &cp->nclist.pattern;
10477 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010478 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010479 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010480 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010481 ap->narg.text = wordtext;
10482 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010483 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010484 break;
10485 app = &ap->narg.next;
10486 readtoken();
10487 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010488 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010489 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010490 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010491 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010492
Eric Andersenc470f442003-07-28 09:56:35 +000010493 cpp = &cp->nclist.next;
10494
10495 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010496 t = readtoken();
10497 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010498 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010499 raise_error_unexpected_syntax(TENDCASE);
10500 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010501 }
Eric Andersenc470f442003-07-28 09:56:35 +000010502 }
Eric Andersencb57d552001-06-28 07:25:16 +000010503 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010504 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010505 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010506 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010507 n1->type = NSUBSHELL;
10508 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010509 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010510 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010511 break;
10512 case TBEGIN:
10513 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010514 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010515 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010516 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010517 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010518 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010519 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010520 }
10521
Eric Andersenc470f442003-07-28 09:56:35 +000010522 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010523 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010524
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010525 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010526 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010527 checkkwd = CHKKWD | CHKALIAS;
10528 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010529 while (readtoken() == TREDIR) {
10530 *rpp = n2 = redirnode;
10531 rpp = &n2->nfile.next;
10532 parsefname();
10533 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010534 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010535 *rpp = NULL;
10536 if (redir) {
10537 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010538 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010539 n2->type = NREDIR;
10540 n2->nredir.n = n1;
10541 n1 = n2;
10542 }
10543 n1->nredir.redirect = redir;
10544 }
Eric Andersencb57d552001-06-28 07:25:16 +000010545 return n1;
10546}
10547
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010548#if ENABLE_ASH_BASH_COMPAT
10549static int decode_dollar_squote(void)
10550{
10551 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10552 int c, cnt;
10553 char *p;
10554 char buf[4];
10555
10556 c = pgetc();
10557 p = strchr(C_escapes, c);
10558 if (p) {
10559 buf[0] = c;
10560 p = buf;
10561 cnt = 3;
10562 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10563 do {
10564 c = pgetc();
10565 *++p = c;
10566 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10567 pungetc();
10568 } else if (c == 'x') { /* \xHH */
10569 do {
10570 c = pgetc();
10571 *++p = c;
10572 } while (isxdigit(c) && --cnt);
10573 pungetc();
10574 if (cnt == 3) { /* \x but next char is "bad" */
10575 c = 'x';
10576 goto unrecognized;
10577 }
10578 } else { /* simple seq like \\ or \t */
10579 p++;
10580 }
10581 *p = '\0';
10582 p = buf;
10583 c = bb_process_escape_sequence((void*)&p);
10584 } else { /* unrecognized "\z": print both chars unless ' or " */
10585 if (c != '\'' && c != '"') {
10586 unrecognized:
10587 c |= 0x100; /* "please encode \, then me" */
10588 }
10589 }
10590 return c;
10591}
10592#endif
10593
Eric Andersencb57d552001-06-28 07:25:16 +000010594/*
10595 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10596 * is not NULL, read a here document. In the latter case, eofmark is the
10597 * word which marks the end of the document and striptabs is true if
10598 * leading tabs should be stripped from the document. The argument firstc
10599 * is the first character of the input token or document.
10600 *
10601 * Because C does not have internal subroutines, I have simulated them
10602 * using goto's to implement the subroutine linkage. The following macros
10603 * will run code that appears at the end of readtoken1.
10604 */
Eric Andersen2870d962001-07-02 17:27:21 +000010605#define CHECKEND() {goto checkend; checkend_return:;}
10606#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10607#define PARSESUB() {goto parsesub; parsesub_return:;}
10608#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10609#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10610#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010611static int
Eric Andersenc470f442003-07-28 09:56:35 +000010612readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010613{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010614 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010615 int c = firstc;
10616 char *out;
10617 int len;
10618 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010619 struct nodelist *bqlist;
10620 smallint quotef;
10621 smallint dblquote;
10622 smallint oldstyle;
10623 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010624#if ENABLE_ASH_EXPAND_PRMT
10625 smallint pssyntax; /* we are expanding a prompt string */
10626#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010627 int varnest; /* levels of variables expansion */
10628 int arinest; /* levels of arithmetic expansion */
10629 int parenlevel; /* levels of parens in arithmetic */
10630 int dqvarnest; /* levels of variables expansion within double quotes */
10631
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010632 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10633
Eric Andersencb57d552001-06-28 07:25:16 +000010634#if __GNUC__
10635 /* Avoid longjmp clobbering */
10636 (void) &out;
10637 (void) &quotef;
10638 (void) &dblquote;
10639 (void) &varnest;
10640 (void) &arinest;
10641 (void) &parenlevel;
10642 (void) &dqvarnest;
10643 (void) &oldstyle;
10644 (void) &prevsyntax;
10645 (void) &syntax;
10646#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010647 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010648 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010649 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010650 oldstyle = 0;
10651 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010652#if ENABLE_ASH_EXPAND_PRMT
10653 pssyntax = (syntax == PSSYNTAX);
10654 if (pssyntax)
10655 syntax = DQSYNTAX;
10656#endif
10657 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010658 varnest = 0;
10659 arinest = 0;
10660 parenlevel = 0;
10661 dqvarnest = 0;
10662
10663 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010664 loop:
10665 /* For each line, until end of word */
10666 {
Eric Andersenc470f442003-07-28 09:56:35 +000010667 CHECKEND(); /* set c to PEOF if at end of here document */
10668 for (;;) { /* until end of line or end of word */
10669 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010670 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010671 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010672 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010673 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010674 USTPUTC(c, out);
10675 plinno++;
10676 if (doprompt)
10677 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010678 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010679 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010680 case CWORD:
10681 USTPUTC(c, out);
10682 break;
10683 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010684 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010685 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010686#if ENABLE_ASH_BASH_COMPAT
10687 if (c == '\\' && bash_dollar_squote) {
10688 c = decode_dollar_squote();
10689 if (c & 0x100) {
10690 USTPUTC('\\', out);
10691 c = (unsigned char)c;
10692 }
10693 }
10694#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010695 USTPUTC(c, out);
10696 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010697 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010698 c = pgetc2();
10699 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010700 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010701 USTPUTC('\\', out);
10702 pungetc();
10703 } else if (c == '\n') {
10704 if (doprompt)
10705 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010706 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010707#if ENABLE_ASH_EXPAND_PRMT
10708 if (c == '$' && pssyntax) {
10709 USTPUTC(CTLESC, out);
10710 USTPUTC('\\', out);
10711 }
10712#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010713 if (dblquote && c != '\\'
10714 && c != '`' && c != '$'
10715 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010716 ) {
10717 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010718 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010719 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010720 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010721 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010722 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010723 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010724 }
10725 break;
10726 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010727 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010728 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010729 if (eofmark == NULL) {
10730 USTPUTC(CTLQUOTEMARK, out);
10731 }
Eric Andersencb57d552001-06-28 07:25:16 +000010732 break;
10733 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010734 syntax = DQSYNTAX;
10735 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010736 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010737 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010738 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010739 if (eofmark != NULL && arinest == 0
10740 && varnest == 0
10741 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010742 USTPUTC(c, out);
10743 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010744 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010745 syntax = BASESYNTAX;
10746 dblquote = 0;
10747 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010748 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010749 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010750 }
10751 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010752 case CVAR: /* '$' */
10753 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010754 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010755 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010756 if (varnest > 0) {
10757 varnest--;
10758 if (dqvarnest > 0) {
10759 dqvarnest--;
10760 }
10761 USTPUTC(CTLENDVAR, out);
10762 } else {
10763 USTPUTC(c, out);
10764 }
10765 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010766#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010767 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010768 parenlevel++;
10769 USTPUTC(c, out);
10770 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010771 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010772 if (parenlevel > 0) {
10773 USTPUTC(c, out);
10774 --parenlevel;
10775 } else {
10776 if (pgetc() == ')') {
10777 if (--arinest == 0) {
10778 USTPUTC(CTLENDARI, out);
10779 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010780 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010781 } else
10782 USTPUTC(')', out);
10783 } else {
10784 /*
10785 * unbalanced parens
10786 * (don't 2nd guess - no error)
10787 */
10788 pungetc();
10789 USTPUTC(')', out);
10790 }
10791 }
10792 break;
10793#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010794 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010795 PARSEBACKQOLD();
10796 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010797 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010798 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010799 case CIGN:
10800 break;
10801 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010802 if (varnest == 0) {
10803#if ENABLE_ASH_BASH_COMPAT
10804 if (c == '&') {
10805 if (pgetc() == '>')
10806 c = 0x100 + '>'; /* flag &> */
10807 pungetc();
10808 }
10809#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010810 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010811 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010812#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010813 if (c != PEOA)
10814#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010815 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010816
Eric Andersencb57d552001-06-28 07:25:16 +000010817 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010818 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010819 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010820 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010821 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010822#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010823 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010824 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010825#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010826 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010827 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010828 if (varnest != 0) {
10829 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010830 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010831 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010832 }
10833 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010834 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010835 out = stackblock();
10836 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010837 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10838 && quotef == 0
10839 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010840 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010841 PARSEREDIR(); /* passed as params: out, c */
10842 lasttoken = TREDIR;
10843 return lasttoken;
10844 }
10845 /* else: non-number X seen, interpret it
10846 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000010847 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010848 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010849 }
10850 quoteflag = quotef;
10851 backquotelist = bqlist;
10852 grabstackblock(len);
10853 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010854 lasttoken = TWORD;
10855 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010856/* end of readtoken routine */
10857
Eric Andersencb57d552001-06-28 07:25:16 +000010858/*
10859 * Check to see whether we are at the end of the here document. When this
10860 * is called, c is set to the first character of the next input line. If
10861 * we are at the end of the here document, this routine sets the c to PEOF.
10862 */
Eric Andersenc470f442003-07-28 09:56:35 +000010863checkend: {
10864 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010865#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010866 if (c == PEOA) {
10867 c = pgetc2();
10868 }
10869#endif
10870 if (striptabs) {
10871 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010872 c = pgetc2();
10873 }
Eric Andersenc470f442003-07-28 09:56:35 +000010874 }
10875 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010876 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010877 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010878
Eric Andersenc470f442003-07-28 09:56:35 +000010879 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010880 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10881 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010882 if (*p == '\n' && *q == '\0') {
10883 c = PEOF;
10884 plinno++;
10885 needprompt = doprompt;
10886 } else {
10887 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010888 }
10889 }
10890 }
10891 }
Eric Andersenc470f442003-07-28 09:56:35 +000010892 goto checkend_return;
10893}
Eric Andersencb57d552001-06-28 07:25:16 +000010894
Eric Andersencb57d552001-06-28 07:25:16 +000010895/*
10896 * Parse a redirection operator. The variable "out" points to a string
10897 * specifying the fd to be redirected. The variable "c" contains the
10898 * first character of the redirection operator.
10899 */
Eric Andersenc470f442003-07-28 09:56:35 +000010900parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010901 /* out is already checked to be a valid number or "" */
10902 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000010903 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010904
Denis Vlasenko597906c2008-02-20 16:38:54 +000010905 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010906 if (c == '>') {
10907 np->nfile.fd = 1;
10908 c = pgetc();
10909 if (c == '>')
10910 np->type = NAPPEND;
10911 else if (c == '|')
10912 np->type = NCLOBBER;
10913 else if (c == '&')
10914 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000010915 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000010916 else {
10917 np->type = NTO;
10918 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010919 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010920 }
10921#if ENABLE_ASH_BASH_COMPAT
10922 else if (c == 0x100 + '>') { /* this flags &> redirection */
10923 np->nfile.fd = 1;
10924 pgetc(); /* this is '>', no need to check */
10925 np->type = NTO2;
10926 }
10927#endif
10928 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010929 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010930 c = pgetc();
10931 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010932 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010933 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010934 np = stzalloc(sizeof(struct nhere));
10935 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010936 }
10937 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010938 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010939 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010940 c = pgetc();
10941 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010942 heredoc->striptabs = 1;
10943 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010944 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010945 pungetc();
10946 }
10947 break;
10948
10949 case '&':
10950 np->type = NFROMFD;
10951 break;
10952
10953 case '>':
10954 np->type = NFROMTO;
10955 break;
10956
10957 default:
10958 np->type = NFROM;
10959 pungetc();
10960 break;
10961 }
Eric Andersencb57d552001-06-28 07:25:16 +000010962 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010963 if (fd >= 0)
10964 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010965 redirnode = np;
10966 goto parseredir_return;
10967}
Eric Andersencb57d552001-06-28 07:25:16 +000010968
Eric Andersencb57d552001-06-28 07:25:16 +000010969/*
10970 * Parse a substitution. At this point, we have read the dollar sign
10971 * and nothing else.
10972 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010973
10974/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10975 * (assuming ascii char codes, as the original implementation did) */
10976#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010977 (((unsigned)(c) - 33 < 32) \
10978 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010979parsesub: {
10980 int subtype;
10981 int typeloc;
10982 int flags;
10983 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010984 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010985
Eric Andersenc470f442003-07-28 09:56:35 +000010986 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010987 if (c <= PEOA_OR_PEOF
10988 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000010989 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010990#if ENABLE_ASH_BASH_COMPAT
10991 if (c == '\'')
10992 bash_dollar_squote = 1;
10993 else
10994#endif
10995 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010996 pungetc();
10997 } else if (c == '(') { /* $(command) or $((arith)) */
10998 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010999#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011000 PARSEARITH();
11001#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011002 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011003#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011004 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011005 pungetc();
11006 PARSEBACKQNEW();
11007 }
11008 } else {
11009 USTPUTC(CTLVAR, out);
11010 typeloc = out - (char *)stackblock();
11011 USTPUTC(VSNORMAL, out);
11012 subtype = VSNORMAL;
11013 if (c == '{') {
11014 c = pgetc();
11015 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011016 c = pgetc();
11017 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011018 c = '#';
11019 else
11020 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011021 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011022 subtype = 0;
11023 }
11024 if (c > PEOA_OR_PEOF && is_name(c)) {
11025 do {
11026 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011027 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011028 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011029 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011030 do {
11031 STPUTC(c, out);
11032 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011033 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011034 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011035 USTPUTC(c, out);
11036 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011037 } else {
11038 badsub:
11039 raise_error_syntax("bad substitution");
11040 }
Eric Andersencb57d552001-06-28 07:25:16 +000011041
Eric Andersenc470f442003-07-28 09:56:35 +000011042 STPUTC('=', out);
11043 flags = 0;
11044 if (subtype == 0) {
11045 switch (c) {
11046 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011047 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011048#if ENABLE_ASH_BASH_COMPAT
11049 if (c == ':' || c == '$' || isdigit(c)) {
11050 pungetc();
11051 subtype = VSSUBSTR;
11052 break;
11053 }
11054#endif
11055 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011056 /*FALLTHROUGH*/
11057 default:
11058 p = strchr(types, c);
11059 if (p == NULL)
11060 goto badsub;
11061 subtype = p - types + VSNORMAL;
11062 break;
11063 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011064 case '#': {
11065 int cc = c;
11066 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11067 c = pgetc();
11068 if (c == cc)
11069 subtype++;
11070 else
11071 pungetc();
11072 break;
11073 }
11074#if ENABLE_ASH_BASH_COMPAT
11075 case '/':
11076 subtype = VSREPLACE;
11077 c = pgetc();
11078 if (c == '/')
11079 subtype++; /* VSREPLACEALL */
11080 else
11081 pungetc();
11082 break;
11083#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011084 }
Eric Andersenc470f442003-07-28 09:56:35 +000011085 } else {
11086 pungetc();
11087 }
11088 if (dblquote || arinest)
11089 flags |= VSQUOTE;
11090 *((char *)stackblock() + typeloc) = subtype | flags;
11091 if (subtype != VSNORMAL) {
11092 varnest++;
11093 if (dblquote || arinest) {
11094 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011095 }
11096 }
11097 }
Eric Andersenc470f442003-07-28 09:56:35 +000011098 goto parsesub_return;
11099}
Eric Andersencb57d552001-06-28 07:25:16 +000011100
Eric Andersencb57d552001-06-28 07:25:16 +000011101/*
11102 * Called to parse command substitutions. Newstyle is set if the command
11103 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11104 * list of commands (passed by reference), and savelen is the number of
11105 * characters on the top of the stack which must be preserved.
11106 */
Eric Andersenc470f442003-07-28 09:56:35 +000011107parsebackq: {
11108 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011109 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011110 union node *n;
11111 char *volatile str;
11112 struct jmploc jmploc;
11113 struct jmploc *volatile savehandler;
11114 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011115 smallint saveprompt = 0;
11116
Eric Andersencb57d552001-06-28 07:25:16 +000011117#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011118 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011119#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011120 savepbq = parsebackquote;
11121 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011122 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011123 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011124 exception_handler = savehandler;
11125 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011126 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011127 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011128 str = NULL;
11129 savelen = out - (char *)stackblock();
11130 if (savelen > 0) {
11131 str = ckmalloc(savelen);
11132 memcpy(str, stackblock(), savelen);
11133 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011134 savehandler = exception_handler;
11135 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011136 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011137 if (oldstyle) {
11138 /* We must read until the closing backquote, giving special
11139 treatment to some slashes, and then push the string and
11140 reread it as input, interpreting it normally. */
11141 char *pout;
11142 int pc;
11143 size_t psavelen;
11144 char *pstr;
11145
11146
11147 STARTSTACKSTR(pout);
11148 for (;;) {
11149 if (needprompt) {
11150 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011151 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011152 pc = pgetc();
11153 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011154 case '`':
11155 goto done;
11156
11157 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011158 pc = pgetc();
11159 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000011160 plinno++;
11161 if (doprompt)
11162 setprompt(2);
11163 /*
11164 * If eating a newline, avoid putting
11165 * the newline into the new character
11166 * stream (via the STPUTC after the
11167 * switch).
11168 */
11169 continue;
11170 }
11171 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011172 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011173 STPUTC('\\', pout);
11174 if (pc > PEOA_OR_PEOF) {
11175 break;
11176 }
11177 /* fall through */
11178
11179 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011180#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011181 case PEOA:
11182#endif
11183 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011184 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011185
11186 case '\n':
11187 plinno++;
11188 needprompt = doprompt;
11189 break;
11190
11191 default:
11192 break;
11193 }
11194 STPUTC(pc, pout);
11195 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011196 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011197 STPUTC('\0', pout);
11198 psavelen = pout - (char *)stackblock();
11199 if (psavelen > 0) {
11200 pstr = grabstackstr(pout);
11201 setinputstring(pstr);
11202 }
11203 }
11204 nlpp = &bqlist;
11205 while (*nlpp)
11206 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011207 *nlpp = stzalloc(sizeof(**nlpp));
11208 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011209 parsebackquote = oldstyle;
11210
11211 if (oldstyle) {
11212 saveprompt = doprompt;
11213 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011214 }
11215
Eric Andersenc470f442003-07-28 09:56:35 +000011216 n = list(2);
11217
11218 if (oldstyle)
11219 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011220 else if (readtoken() != TRP)
11221 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011222
11223 (*nlpp)->n = n;
11224 if (oldstyle) {
11225 /*
11226 * Start reading from old file again, ignoring any pushed back
11227 * tokens left from the backquote parsing
11228 */
11229 popfile();
11230 tokpushback = 0;
11231 }
11232 while (stackblocksize() <= savelen)
11233 growstackblock();
11234 STARTSTACKSTR(out);
11235 if (str) {
11236 memcpy(out, str, savelen);
11237 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011238 INT_OFF;
11239 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011240 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011241 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011242 }
11243 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011244 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011245 if (arinest || dblquote)
11246 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11247 else
11248 USTPUTC(CTLBACKQ, out);
11249 if (oldstyle)
11250 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011251 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011252}
11253
Denis Vlasenko131ae172007-02-18 13:00:19 +000011254#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011255/*
11256 * Parse an arithmetic expansion (indicate start of one and set state)
11257 */
Eric Andersenc470f442003-07-28 09:56:35 +000011258parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011259 if (++arinest == 1) {
11260 prevsyntax = syntax;
11261 syntax = ARISYNTAX;
11262 USTPUTC(CTLARI, out);
11263 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011264 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011265 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011266 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011267 } else {
11268 /*
11269 * we collapse embedded arithmetic expansion to
11270 * parenthesis, which should be equivalent
11271 */
11272 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011273 }
Eric Andersenc470f442003-07-28 09:56:35 +000011274 goto parsearith_return;
11275}
11276#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011277
Eric Andersenc470f442003-07-28 09:56:35 +000011278} /* end of readtoken */
11279
Eric Andersencb57d552001-06-28 07:25:16 +000011280/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011281 * Read the next input token.
11282 * If the token is a word, we set backquotelist to the list of cmds in
11283 * backquotes. We set quoteflag to true if any part of the word was
11284 * quoted.
11285 * If the token is TREDIR, then we set redirnode to a structure containing
11286 * the redirection.
11287 * In all cases, the variable startlinno is set to the number of the line
11288 * on which the token starts.
11289 *
11290 * [Change comment: here documents and internal procedures]
11291 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11292 * word parsing code into a separate routine. In this case, readtoken
11293 * doesn't need to have any internal procedures, but parseword does.
11294 * We could also make parseoperator in essence the main routine, and
11295 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011296 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011297#define NEW_xxreadtoken
11298#ifdef NEW_xxreadtoken
11299/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011300static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011301 '\n', '(', ')', /* singles */
11302 '&', '|', ';', /* doubles */
11303 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011304};
Eric Andersencb57d552001-06-28 07:25:16 +000011305
Denis Vlasenko834dee72008-10-07 09:18:30 +000011306#define xxreadtoken_singles 3
11307#define xxreadtoken_doubles 3
11308
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011309static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011310 TNL, TLP, TRP, /* only single occurrence allowed */
11311 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11312 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011313 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011314};
11315
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011316static int
11317xxreadtoken(void)
11318{
11319 int c;
11320
11321 if (tokpushback) {
11322 tokpushback = 0;
11323 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011324 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011325 if (needprompt) {
11326 setprompt(2);
11327 }
11328 startlinno = plinno;
11329 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011330 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011331 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11332 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011333
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011334 if (c == '#') {
11335 while ((c = pgetc()) != '\n' && c != PEOF)
11336 continue;
11337 pungetc();
11338 } else if (c == '\\') {
11339 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011340 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011341 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011342 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011343 startlinno = ++plinno;
11344 if (doprompt)
11345 setprompt(2);
11346 } else {
11347 const char *p;
11348
11349 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11350 if (c != PEOF) {
11351 if (c == '\n') {
11352 plinno++;
11353 needprompt = doprompt;
11354 }
11355
11356 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011357 if (p == NULL)
11358 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011359
Denis Vlasenko834dee72008-10-07 09:18:30 +000011360 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11361 int cc = pgetc();
11362 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011363 p += xxreadtoken_doubles + 1;
11364 } else {
11365 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011366#if ENABLE_ASH_BASH_COMPAT
11367 if (c == '&' && cc == '>') /* &> */
11368 break; /* return readtoken1(...) */
11369#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011370 }
11371 }
11372 }
11373 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11374 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011375 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011376 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011377
11378 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011379}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011380#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011381#define RETURN(token) return lasttoken = token
11382static int
11383xxreadtoken(void)
11384{
11385 int c;
11386
11387 if (tokpushback) {
11388 tokpushback = 0;
11389 return lasttoken;
11390 }
11391 if (needprompt) {
11392 setprompt(2);
11393 }
11394 startlinno = plinno;
11395 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011396 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011397 switch (c) {
11398 case ' ': case '\t':
11399#if ENABLE_ASH_ALIAS
11400 case PEOA:
11401#endif
11402 continue;
11403 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011404 while ((c = pgetc()) != '\n' && c != PEOF)
11405 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011406 pungetc();
11407 continue;
11408 case '\\':
11409 if (pgetc() == '\n') {
11410 startlinno = ++plinno;
11411 if (doprompt)
11412 setprompt(2);
11413 continue;
11414 }
11415 pungetc();
11416 goto breakloop;
11417 case '\n':
11418 plinno++;
11419 needprompt = doprompt;
11420 RETURN(TNL);
11421 case PEOF:
11422 RETURN(TEOF);
11423 case '&':
11424 if (pgetc() == '&')
11425 RETURN(TAND);
11426 pungetc();
11427 RETURN(TBACKGND);
11428 case '|':
11429 if (pgetc() == '|')
11430 RETURN(TOR);
11431 pungetc();
11432 RETURN(TPIPE);
11433 case ';':
11434 if (pgetc() == ';')
11435 RETURN(TENDCASE);
11436 pungetc();
11437 RETURN(TSEMI);
11438 case '(':
11439 RETURN(TLP);
11440 case ')':
11441 RETURN(TRP);
11442 default:
11443 goto breakloop;
11444 }
11445 }
11446 breakloop:
11447 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11448#undef RETURN
11449}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011450#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011451
11452static int
11453readtoken(void)
11454{
11455 int t;
11456#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011457 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011458#endif
11459
11460#if ENABLE_ASH_ALIAS
11461 top:
11462#endif
11463
11464 t = xxreadtoken();
11465
11466 /*
11467 * eat newlines
11468 */
11469 if (checkkwd & CHKNL) {
11470 while (t == TNL) {
11471 parseheredoc();
11472 t = xxreadtoken();
11473 }
11474 }
11475
11476 if (t != TWORD || quoteflag) {
11477 goto out;
11478 }
11479
11480 /*
11481 * check for keywords
11482 */
11483 if (checkkwd & CHKKWD) {
11484 const char *const *pp;
11485
11486 pp = findkwd(wordtext);
11487 if (pp) {
11488 lasttoken = t = pp - tokname_array;
11489 TRACE(("keyword %s recognized\n", tokname(t)));
11490 goto out;
11491 }
11492 }
11493
11494 if (checkkwd & CHKALIAS) {
11495#if ENABLE_ASH_ALIAS
11496 struct alias *ap;
11497 ap = lookupalias(wordtext, 1);
11498 if (ap != NULL) {
11499 if (*ap->val) {
11500 pushstring(ap->val, ap);
11501 }
11502 goto top;
11503 }
11504#endif
11505 }
11506 out:
11507 checkkwd = 0;
11508#if DEBUG
11509 if (!alreadyseen)
11510 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11511 else
11512 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11513#endif
11514 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011515}
11516
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011517static char
11518peektoken(void)
11519{
11520 int t;
11521
11522 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011523 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011524 return tokname_array[t][0];
11525}
Eric Andersencb57d552001-06-28 07:25:16 +000011526
11527/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011528 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11529 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011530 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011531static union node *
11532parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011533{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011534 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011535
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011536 tokpushback = 0;
11537 doprompt = interact;
11538 if (doprompt)
11539 setprompt(doprompt);
11540 needprompt = 0;
11541 t = readtoken();
11542 if (t == TEOF)
11543 return NEOF;
11544 if (t == TNL)
11545 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011546 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011547 return list(1);
11548}
11549
11550/*
11551 * Input any here documents.
11552 */
11553static void
11554parseheredoc(void)
11555{
11556 struct heredoc *here;
11557 union node *n;
11558
11559 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011560 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011561
11562 while (here) {
11563 if (needprompt) {
11564 setprompt(2);
11565 }
11566 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11567 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011568 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011569 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011570 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011571 n->narg.text = wordtext;
11572 n->narg.backquote = backquotelist;
11573 here->here->nhere.doc = n;
11574 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011575 }
Eric Andersencb57d552001-06-28 07:25:16 +000011576}
11577
11578
11579/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011580 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011581 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011582#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011583static const char *
11584expandstr(const char *ps)
11585{
11586 union node n;
11587
11588 /* XXX Fix (char *) cast. */
11589 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011590 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011591 popfile();
11592
11593 n.narg.type = NARG;
11594 n.narg.next = NULL;
11595 n.narg.text = wordtext;
11596 n.narg.backquote = backquotelist;
11597
11598 expandarg(&n, NULL, 0);
11599 return stackblock();
11600}
11601#endif
11602
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011603/*
11604 * Execute a command or commands contained in a string.
11605 */
11606static int
11607evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011608{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011609 union node *n;
11610 struct stackmark smark;
11611 int skip;
11612
11613 setinputstring(s);
11614 setstackmark(&smark);
11615
11616 skip = 0;
11617 while ((n = parsecmd(0)) != NEOF) {
11618 evaltree(n, 0);
11619 popstackmark(&smark);
11620 skip = evalskip;
11621 if (skip)
11622 break;
11623 }
11624 popfile();
11625
11626 skip &= mask;
11627 evalskip = skip;
11628 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011629}
11630
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011631/*
11632 * The eval command.
11633 */
11634static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011635evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011636{
11637 char *p;
11638 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011639
Denis Vlasenko68404f12008-03-17 09:00:54 +000011640 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011641 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011642 argv += 2;
11643 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011644 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011645 for (;;) {
11646 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011647 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011648 if (p == NULL)
11649 break;
11650 STPUTC(' ', concat);
11651 }
11652 STPUTC('\0', concat);
11653 p = grabstackstr(concat);
11654 }
11655 evalstring(p, ~SKIPEVAL);
11656
11657 }
11658 return exitstatus;
11659}
11660
11661/*
11662 * Read and execute commands. "Top" is nonzero for the top level command
11663 * loop; it turns on prompting if the shell is interactive.
11664 */
11665static int
11666cmdloop(int top)
11667{
11668 union node *n;
11669 struct stackmark smark;
11670 int inter;
11671 int numeof = 0;
11672
11673 TRACE(("cmdloop(%d) called\n", top));
11674 for (;;) {
11675 int skip;
11676
11677 setstackmark(&smark);
11678#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011679 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011680 showjobs(stderr, SHOW_CHANGED);
11681#endif
11682 inter = 0;
11683 if (iflag && top) {
11684 inter++;
11685#if ENABLE_ASH_MAIL
11686 chkmail();
11687#endif
11688 }
11689 n = parsecmd(inter);
11690 /* showtree(n); DEBUG */
11691 if (n == NEOF) {
11692 if (!top || numeof >= 50)
11693 break;
11694 if (!stoppedjobs()) {
11695 if (!Iflag)
11696 break;
11697 out2str("\nUse \"exit\" to leave shell.\n");
11698 }
11699 numeof++;
11700 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011701 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11702 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011703 numeof = 0;
11704 evaltree(n, 0);
11705 }
11706 popstackmark(&smark);
11707 skip = evalskip;
11708
11709 if (skip) {
11710 evalskip = 0;
11711 return skip & SKIPEVAL;
11712 }
11713 }
11714 return 0;
11715}
11716
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011717/*
11718 * Take commands from a file. To be compatible we should do a path
11719 * search for the file, which is necessary to find sub-commands.
11720 */
11721static char *
11722find_dot_file(char *name)
11723{
11724 char *fullname;
11725 const char *path = pathval();
11726 struct stat statb;
11727
11728 /* don't try this for absolute or relative paths */
11729 if (strchr(name, '/'))
11730 return name;
11731
11732 while ((fullname = padvance(&path, name)) != NULL) {
11733 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11734 /*
11735 * Don't bother freeing here, since it will
11736 * be freed by the caller.
11737 */
11738 return fullname;
11739 }
11740 stunalloc(fullname);
11741 }
11742
11743 /* not found in the PATH */
11744 ash_msg_and_raise_error("%s: not found", name);
11745 /* NOTREACHED */
11746}
11747
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011748static int
11749dotcmd(int argc, char **argv)
11750{
11751 struct strlist *sp;
11752 volatile struct shparam saveparam;
11753 int status = 0;
11754
11755 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011756 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011757
Denis Vlasenko68404f12008-03-17 09:00:54 +000011758 if (argv[1]) { /* That's what SVR2 does */
11759 char *fullname = find_dot_file(argv[1]);
11760 argv += 2;
11761 argc -= 2;
11762 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011763 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011764 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011765 shellparam.nparam = argc;
11766 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011767 };
11768
11769 setinputfile(fullname, INPUT_PUSH_FILE);
11770 commandname = fullname;
11771 cmdloop(0);
11772 popfile();
11773
Denis Vlasenko68404f12008-03-17 09:00:54 +000011774 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011775 freeparam(&shellparam);
11776 shellparam = saveparam;
11777 };
11778 status = exitstatus;
11779 }
11780 return status;
11781}
11782
11783static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011784exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011785{
11786 if (stoppedjobs())
11787 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011788 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011789 exitstatus = number(argv[1]);
11790 raise_exception(EXEXIT);
11791 /* NOTREACHED */
11792}
11793
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011794/*
11795 * Read a file containing shell functions.
11796 */
11797static void
11798readcmdfile(char *name)
11799{
11800 setinputfile(name, INPUT_PUSH_FILE);
11801 cmdloop(0);
11802 popfile();
11803}
11804
11805
Denis Vlasenkocc571512007-02-23 21:10:35 +000011806/* ============ find_command inplementation */
11807
11808/*
11809 * Resolve a command name. If you change this routine, you may have to
11810 * change the shellexec routine as well.
11811 */
11812static void
11813find_command(char *name, struct cmdentry *entry, int act, const char *path)
11814{
11815 struct tblentry *cmdp;
11816 int idx;
11817 int prev;
11818 char *fullname;
11819 struct stat statb;
11820 int e;
11821 int updatetbl;
11822 struct builtincmd *bcmd;
11823
11824 /* If name contains a slash, don't use PATH or hash table */
11825 if (strchr(name, '/') != NULL) {
11826 entry->u.index = -1;
11827 if (act & DO_ABS) {
11828 while (stat(name, &statb) < 0) {
11829#ifdef SYSV
11830 if (errno == EINTR)
11831 continue;
11832#endif
11833 entry->cmdtype = CMDUNKNOWN;
11834 return;
11835 }
11836 }
11837 entry->cmdtype = CMDNORMAL;
11838 return;
11839 }
11840
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011841/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011842
11843 updatetbl = (path == pathval());
11844 if (!updatetbl) {
11845 act |= DO_ALTPATH;
11846 if (strstr(path, "%builtin") != NULL)
11847 act |= DO_ALTBLTIN;
11848 }
11849
11850 /* If name is in the table, check answer will be ok */
11851 cmdp = cmdlookup(name, 0);
11852 if (cmdp != NULL) {
11853 int bit;
11854
11855 switch (cmdp->cmdtype) {
11856 default:
11857#if DEBUG
11858 abort();
11859#endif
11860 case CMDNORMAL:
11861 bit = DO_ALTPATH;
11862 break;
11863 case CMDFUNCTION:
11864 bit = DO_NOFUNC;
11865 break;
11866 case CMDBUILTIN:
11867 bit = DO_ALTBLTIN;
11868 break;
11869 }
11870 if (act & bit) {
11871 updatetbl = 0;
11872 cmdp = NULL;
11873 } else if (cmdp->rehash == 0)
11874 /* if not invalidated by cd, we're done */
11875 goto success;
11876 }
11877
11878 /* If %builtin not in path, check for builtin next */
11879 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011880 if (bcmd) {
11881 if (IS_BUILTIN_REGULAR(bcmd))
11882 goto builtin_success;
11883 if (act & DO_ALTPATH) {
11884 if (!(act & DO_ALTBLTIN))
11885 goto builtin_success;
11886 } else if (builtinloc <= 0) {
11887 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011888 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011889 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011890
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011891#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000011892 {
11893 int applet_no = find_applet_by_name(name);
11894 if (applet_no >= 0) {
11895 entry->cmdtype = CMDNORMAL;
11896 entry->u.index = -2 - applet_no;
11897 return;
11898 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011899 }
11900#endif
11901
Denis Vlasenkocc571512007-02-23 21:10:35 +000011902 /* We have to search path. */
11903 prev = -1; /* where to start */
11904 if (cmdp && cmdp->rehash) { /* doing a rehash */
11905 if (cmdp->cmdtype == CMDBUILTIN)
11906 prev = builtinloc;
11907 else
11908 prev = cmdp->param.index;
11909 }
11910
11911 e = ENOENT;
11912 idx = -1;
11913 loop:
11914 while ((fullname = padvance(&path, name)) != NULL) {
11915 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011916 /* NB: code below will still use fullname
11917 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011918 idx++;
11919 if (pathopt) {
11920 if (prefix(pathopt, "builtin")) {
11921 if (bcmd)
11922 goto builtin_success;
11923 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000011924 }
11925 if ((act & DO_NOFUNC)
11926 || !prefix(pathopt, "func")
11927 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011928 continue;
11929 }
11930 }
11931 /* if rehash, don't redo absolute path names */
11932 if (fullname[0] == '/' && idx <= prev) {
11933 if (idx < prev)
11934 continue;
11935 TRACE(("searchexec \"%s\": no change\n", name));
11936 goto success;
11937 }
11938 while (stat(fullname, &statb) < 0) {
11939#ifdef SYSV
11940 if (errno == EINTR)
11941 continue;
11942#endif
11943 if (errno != ENOENT && errno != ENOTDIR)
11944 e = errno;
11945 goto loop;
11946 }
11947 e = EACCES; /* if we fail, this will be the error */
11948 if (!S_ISREG(statb.st_mode))
11949 continue;
11950 if (pathopt) { /* this is a %func directory */
11951 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011952 /* NB: stalloc will return space pointed by fullname
11953 * (because we don't have any intervening allocations
11954 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011955 readcmdfile(fullname);
11956 cmdp = cmdlookup(name, 0);
11957 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11958 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11959 stunalloc(fullname);
11960 goto success;
11961 }
11962 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11963 if (!updatetbl) {
11964 entry->cmdtype = CMDNORMAL;
11965 entry->u.index = idx;
11966 return;
11967 }
11968 INT_OFF;
11969 cmdp = cmdlookup(name, 1);
11970 cmdp->cmdtype = CMDNORMAL;
11971 cmdp->param.index = idx;
11972 INT_ON;
11973 goto success;
11974 }
11975
11976 /* We failed. If there was an entry for this command, delete it */
11977 if (cmdp && updatetbl)
11978 delete_cmd_entry();
11979 if (act & DO_ERR)
11980 ash_msg("%s: %s", name, errmsg(e, "not found"));
11981 entry->cmdtype = CMDUNKNOWN;
11982 return;
11983
11984 builtin_success:
11985 if (!updatetbl) {
11986 entry->cmdtype = CMDBUILTIN;
11987 entry->u.cmd = bcmd;
11988 return;
11989 }
11990 INT_OFF;
11991 cmdp = cmdlookup(name, 1);
11992 cmdp->cmdtype = CMDBUILTIN;
11993 cmdp->param.cmd = bcmd;
11994 INT_ON;
11995 success:
11996 cmdp->rehash = 0;
11997 entry->cmdtype = cmdp->cmdtype;
11998 entry->u = cmdp->param;
11999}
12000
12001
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012002/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012003
Eric Andersencb57d552001-06-28 07:25:16 +000012004/*
Eric Andersencb57d552001-06-28 07:25:16 +000012005 * The trap builtin.
12006 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012007static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012008trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012009{
12010 char *action;
12011 char **ap;
12012 int signo;
12013
Eric Andersenc470f442003-07-28 09:56:35 +000012014 nextopt(nullstr);
12015 ap = argptr;
12016 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012017 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012018 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012019 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012020 single_quote(trap[signo]),
12021 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012022 }
12023 }
12024 return 0;
12025 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012026 action = NULL;
12027 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012028 action = *ap++;
12029 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012030 signo = get_signum(*ap);
12031 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012032 ash_msg_and_raise_error("%s: bad trap", *ap);
12033 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012034 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012035 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012036 action = NULL;
12037 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012038 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012039 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012040 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012041 trap[signo] = action;
12042 if (signo != 0)
12043 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012044 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012045 ap++;
12046 }
12047 return 0;
12048}
12049
Eric Andersenc470f442003-07-28 09:56:35 +000012050
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012051/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012052
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012053#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012054/*
12055 * Lists available builtins
12056 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012057static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012058helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012059{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012060 unsigned col;
12061 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012062
12063 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012064 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012065 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012066 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012067 if (col > 60) {
12068 out1fmt("\n");
12069 col = 0;
12070 }
12071 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012072#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012073 {
12074 const char *a = applet_names;
12075 while (*a) {
12076 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12077 if (col > 60) {
12078 out1fmt("\n");
12079 col = 0;
12080 }
12081 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012082 }
12083 }
12084#endif
12085 out1fmt("\n\n");
12086 return EXIT_SUCCESS;
12087}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012088#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012089
Eric Andersencb57d552001-06-28 07:25:16 +000012090/*
Eric Andersencb57d552001-06-28 07:25:16 +000012091 * The export and readonly commands.
12092 */
Eric Andersenc470f442003-07-28 09:56:35 +000012093static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012094exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012095{
12096 struct var *vp;
12097 char *name;
12098 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012099 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012100 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012101
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012102 if (nextopt("p") != 'p') {
12103 aptr = argptr;
12104 name = *aptr;
12105 if (name) {
12106 do {
12107 p = strchr(name, '=');
12108 if (p != NULL) {
12109 p++;
12110 } else {
12111 vp = *findvar(hashvar(name), name);
12112 if (vp) {
12113 vp->flags |= flag;
12114 continue;
12115 }
Eric Andersencb57d552001-06-28 07:25:16 +000012116 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012117 setvar(name, p, flag);
12118 } while ((name = *++aptr) != NULL);
12119 return 0;
12120 }
Eric Andersencb57d552001-06-28 07:25:16 +000012121 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012122 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012123 return 0;
12124}
12125
Eric Andersencb57d552001-06-28 07:25:16 +000012126/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012127 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012128 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012129static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012130unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012131{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012132 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012133
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012134 cmdp = cmdlookup(name, 0);
12135 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12136 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012137}
12138
Eric Andersencb57d552001-06-28 07:25:16 +000012139/*
Eric Andersencb57d552001-06-28 07:25:16 +000012140 * The unset builtin command. We unset the function before we unset the
12141 * variable to allow a function to be unset when there is a readonly variable
12142 * with the same name.
12143 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012144static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012145unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012146{
12147 char **ap;
12148 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012149 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012150 int ret = 0;
12151
12152 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012153 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012154 }
Eric Andersencb57d552001-06-28 07:25:16 +000012155
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012156 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012157 if (flag != 'f') {
12158 i = unsetvar(*ap);
12159 ret |= i;
12160 if (!(i & 2))
12161 continue;
12162 }
12163 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012164 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012165 }
Eric Andersenc470f442003-07-28 09:56:35 +000012166 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012167}
12168
12169
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012170/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012171
Eric Andersenc470f442003-07-28 09:56:35 +000012172#include <sys/times.h>
12173
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012174static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012175 ' ', offsetof(struct tms, tms_utime),
12176 '\n', offsetof(struct tms, tms_stime),
12177 ' ', offsetof(struct tms, tms_cutime),
12178 '\n', offsetof(struct tms, tms_cstime),
12179 0
12180};
Eric Andersencb57d552001-06-28 07:25:16 +000012181
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012182static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012183timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012184{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012185 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012186 const unsigned char *p;
12187 struct tms buf;
12188
12189 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012190 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012191
12192 p = timescmd_str;
12193 do {
12194 t = *(clock_t *)(((char *) &buf) + p[1]);
12195 s = t / clk_tck;
12196 out1fmt("%ldm%ld.%.3lds%c",
12197 s/60, s%60,
12198 ((t - s * clk_tck) * 1000) / clk_tck,
12199 p[0]);
12200 } while (*(p += 2));
12201
Eric Andersencb57d552001-06-28 07:25:16 +000012202 return 0;
12203}
12204
Denis Vlasenko131ae172007-02-18 13:00:19 +000012205#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012206static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012207dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012208{
Eric Andersened9ecf72004-06-22 08:29:45 +000012209 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012210 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012211
Denis Vlasenkob012b102007-02-19 22:43:01 +000012212 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012213 result = arith(s, &errcode);
12214 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012215 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012216 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012217 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012218 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012219 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012220 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012221 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012222 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012223 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012224
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012225 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012226}
Eric Andersenc470f442003-07-28 09:56:35 +000012227
Eric Andersenc470f442003-07-28 09:56:35 +000012228/*
Eric Andersen90898442003-08-06 11:20:52 +000012229 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12230 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12231 *
12232 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012233 */
12234static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012235letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012236{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012237 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012238
Denis Vlasenko68404f12008-03-17 09:00:54 +000012239 argv++;
12240 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012241 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012242 do {
12243 i = dash_arith(*argv);
12244 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012245
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012246 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012247}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012248#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012249
Eric Andersenc470f442003-07-28 09:56:35 +000012250
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012251/* ============ miscbltin.c
12252 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012253 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012254 */
12255
12256#undef rflag
12257
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012258#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012259typedef enum __rlimit_resource rlim_t;
12260#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012261
Eric Andersenc470f442003-07-28 09:56:35 +000012262/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012263 * The read builtin. Options:
12264 * -r Do not interpret '\' specially
12265 * -s Turn off echo (tty only)
12266 * -n NCHARS Read NCHARS max
12267 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12268 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12269 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012270 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012271 * TODO: bash also has:
12272 * -a ARRAY Read into array[0],[1],etc
12273 * -d DELIM End on DELIM char, not newline
12274 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012275 */
Eric Andersenc470f442003-07-28 09:56:35 +000012276static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012277readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012278{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012279 static const char *const arg_REPLY[] = { "REPLY", NULL };
12280
Eric Andersenc470f442003-07-28 09:56:35 +000012281 char **ap;
12282 int backslash;
12283 char c;
12284 int rflag;
12285 char *prompt;
12286 const char *ifs;
12287 char *p;
12288 int startword;
12289 int status;
12290 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012291 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012292#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012293 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012294 int silent = 0;
12295 struct termios tty, old_tty;
12296#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012297#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012298 unsigned end_ms = 0;
12299 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012300#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012301
12302 rflag = 0;
12303 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012304 while ((i = nextopt("p:u:r"
12305 USE_ASH_READ_TIMEOUT("t:")
12306 USE_ASH_READ_NCHARS("n:s")
12307 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012308 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012309 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012310 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012311 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012312#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012313 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012314 nchars = bb_strtou(optionarg, NULL, 10);
12315 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012316 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012317 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012318 break;
12319 case 's':
12320 silent = 1;
12321 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012322#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012323#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012324 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012325 timeout = bb_strtou(optionarg, NULL, 10);
12326 if (errno || timeout > UINT_MAX / 2048)
12327 ash_msg_and_raise_error("invalid timeout");
12328 timeout *= 1000;
12329#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012330 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012331 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012332 /* EINVAL means number is ok, but not terminated by NUL */
12333 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012334 char *p2;
12335 if (*++p) {
12336 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012337 ts.tv_usec = bb_strtou(p, &p2, 10);
12338 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012339 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012340 scale = p2 - p;
12341 /* normalize to usec */
12342 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012343 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012344 while (scale++ < 6)
12345 ts.tv_usec *= 10;
12346 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012347 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012348 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012349 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012350 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012351 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012352 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012353#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012354 break;
12355#endif
12356 case 'r':
12357 rflag = 1;
12358 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012359 case 'u':
12360 fd = bb_strtou(optionarg, NULL, 10);
12361 if (fd < 0 || errno)
12362 ash_msg_and_raise_error("invalid file descriptor");
12363 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012364 default:
12365 break;
12366 }
Eric Andersenc470f442003-07-28 09:56:35 +000012367 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012368 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012369 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012370 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012371 ap = argptr;
12372 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012373 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012374 ifs = bltinlookup("IFS");
12375 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012376 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012377#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012378 tcgetattr(fd, &tty);
12379 old_tty = tty;
12380 if (nchars || silent) {
12381 if (nchars) {
12382 tty.c_lflag &= ~ICANON;
12383 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012384 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012385 if (silent) {
12386 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12387 }
12388 /* if tcgetattr failed, tcsetattr will fail too.
12389 * Ignoring, it's harmless. */
12390 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012391 }
12392#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012393
Eric Andersenc470f442003-07-28 09:56:35 +000012394 status = 0;
12395 startword = 1;
12396 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012397#if ENABLE_ASH_READ_TIMEOUT
12398 if (timeout) /* NB: ensuring end_ms is nonzero */
12399 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12400#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012401 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012402 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012403#if ENABLE_ASH_READ_TIMEOUT
12404 if (end_ms) {
12405 struct pollfd pfd[1];
12406 pfd[0].fd = fd;
12407 pfd[0].events = POLLIN;
12408 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12409 if ((int)timeout <= 0 /* already late? */
12410 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12411 ) { /* timed out! */
12412#if ENABLE_ASH_READ_NCHARS
12413 tcsetattr(fd, TCSANOW, &old_tty);
12414#endif
12415 return 1;
12416 }
12417 }
12418#endif
12419 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012420 status = 1;
12421 break;
12422 }
12423 if (c == '\0')
12424 continue;
12425 if (backslash) {
12426 backslash = 0;
12427 if (c != '\n')
12428 goto put;
12429 continue;
12430 }
12431 if (!rflag && c == '\\') {
12432 backslash++;
12433 continue;
12434 }
12435 if (c == '\n')
12436 break;
12437 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12438 continue;
12439 }
12440 startword = 0;
12441 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12442 STACKSTRNUL(p);
12443 setvar(*ap, stackblock(), 0);
12444 ap++;
12445 startword = 1;
12446 STARTSTACKSTR(p);
12447 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012448 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012449 STPUTC(c, p);
12450 }
12451 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012452/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012453#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012454 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012455#else
12456 while (1);
12457#endif
12458
12459#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012460 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012461#endif
12462
Eric Andersenc470f442003-07-28 09:56:35 +000012463 STACKSTRNUL(p);
12464 /* Remove trailing blanks */
12465 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12466 *p = '\0';
12467 setvar(*ap, stackblock(), 0);
12468 while (*++ap != NULL)
12469 setvar(*ap, nullstr, 0);
12470 return status;
12471}
12472
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012473static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012474umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012475{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012476 static const char permuser[3] ALIGN1 = "ugo";
12477 static const char permmode[3] ALIGN1 = "rwx";
12478 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012479 S_IRUSR, S_IWUSR, S_IXUSR,
12480 S_IRGRP, S_IWGRP, S_IXGRP,
12481 S_IROTH, S_IWOTH, S_IXOTH
12482 };
12483
12484 char *ap;
12485 mode_t mask;
12486 int i;
12487 int symbolic_mode = 0;
12488
12489 while (nextopt("S") != '\0') {
12490 symbolic_mode = 1;
12491 }
12492
Denis Vlasenkob012b102007-02-19 22:43:01 +000012493 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012494 mask = umask(0);
12495 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012496 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012497
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012498 ap = *argptr;
12499 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012500 if (symbolic_mode) {
12501 char buf[18];
12502 char *p = buf;
12503
12504 for (i = 0; i < 3; i++) {
12505 int j;
12506
12507 *p++ = permuser[i];
12508 *p++ = '=';
12509 for (j = 0; j < 3; j++) {
12510 if ((mask & permmask[3 * i + j]) == 0) {
12511 *p++ = permmode[j];
12512 }
12513 }
12514 *p++ = ',';
12515 }
12516 *--p = 0;
12517 puts(buf);
12518 } else {
12519 out1fmt("%.4o\n", mask);
12520 }
12521 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012522 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012523 mask = 0;
12524 do {
12525 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012526 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012527 mask = (mask << 3) + (*ap - '0');
12528 } while (*++ap != '\0');
12529 umask(mask);
12530 } else {
12531 mask = ~mask & 0777;
12532 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012533 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012534 }
12535 umask(~mask & 0777);
12536 }
12537 }
12538 return 0;
12539}
12540
12541/*
12542 * ulimit builtin
12543 *
12544 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12545 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12546 * ash by J.T. Conklin.
12547 *
12548 * Public domain.
12549 */
12550
12551struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012552 uint8_t cmd; /* RLIMIT_xxx fit into it */
12553 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012554 char option;
12555};
12556
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012557static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012558#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012559 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012560#endif
12561#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012562 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012563#endif
12564#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012565 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012566#endif
12567#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012568 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012569#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012570#ifdef RLIMIT_CORE
12571 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012572#endif
12573#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012574 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012575#endif
12576#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012577 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012578#endif
12579#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012580 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012581#endif
12582#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012583 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012584#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012585#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012586 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012587#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012588#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012589 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012590#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012591};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012592static const char limits_name[] =
12593#ifdef RLIMIT_CPU
12594 "time(seconds)" "\0"
12595#endif
12596#ifdef RLIMIT_FSIZE
12597 "file(blocks)" "\0"
12598#endif
12599#ifdef RLIMIT_DATA
12600 "data(kb)" "\0"
12601#endif
12602#ifdef RLIMIT_STACK
12603 "stack(kb)" "\0"
12604#endif
12605#ifdef RLIMIT_CORE
12606 "coredump(blocks)" "\0"
12607#endif
12608#ifdef RLIMIT_RSS
12609 "memory(kb)" "\0"
12610#endif
12611#ifdef RLIMIT_MEMLOCK
12612 "locked memory(kb)" "\0"
12613#endif
12614#ifdef RLIMIT_NPROC
12615 "process" "\0"
12616#endif
12617#ifdef RLIMIT_NOFILE
12618 "nofiles" "\0"
12619#endif
12620#ifdef RLIMIT_AS
12621 "vmemory(kb)" "\0"
12622#endif
12623#ifdef RLIMIT_LOCKS
12624 "locks" "\0"
12625#endif
12626;
Eric Andersenc470f442003-07-28 09:56:35 +000012627
Glenn L McGrath76620622004-01-13 10:19:37 +000012628enum limtype { SOFT = 0x1, HARD = 0x2 };
12629
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012630static void
12631printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012632 const struct limits *l)
12633{
12634 rlim_t val;
12635
12636 val = limit->rlim_max;
12637 if (how & SOFT)
12638 val = limit->rlim_cur;
12639
12640 if (val == RLIM_INFINITY)
12641 out1fmt("unlimited\n");
12642 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012643 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012644 out1fmt("%lld\n", (long long) val);
12645 }
12646}
12647
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012648static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012649ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012650{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012651 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012652 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012653 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012654 const struct limits *l;
12655 int set, all = 0;
12656 int optc, what;
12657 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012658
12659 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012660 while ((optc = nextopt("HSa"
12661#ifdef RLIMIT_CPU
12662 "t"
12663#endif
12664#ifdef RLIMIT_FSIZE
12665 "f"
12666#endif
12667#ifdef RLIMIT_DATA
12668 "d"
12669#endif
12670#ifdef RLIMIT_STACK
12671 "s"
12672#endif
12673#ifdef RLIMIT_CORE
12674 "c"
12675#endif
12676#ifdef RLIMIT_RSS
12677 "m"
12678#endif
12679#ifdef RLIMIT_MEMLOCK
12680 "l"
12681#endif
12682#ifdef RLIMIT_NPROC
12683 "p"
12684#endif
12685#ifdef RLIMIT_NOFILE
12686 "n"
12687#endif
12688#ifdef RLIMIT_AS
12689 "v"
12690#endif
12691#ifdef RLIMIT_LOCKS
12692 "w"
12693#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012694 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012695 switch (optc) {
12696 case 'H':
12697 how = HARD;
12698 break;
12699 case 'S':
12700 how = SOFT;
12701 break;
12702 case 'a':
12703 all = 1;
12704 break;
12705 default:
12706 what = optc;
12707 }
12708
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012709 for (l = limits_tbl; l->option != what; l++)
12710 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012711
12712 set = *argptr ? 1 : 0;
12713 if (set) {
12714 char *p = *argptr;
12715
12716 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012717 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012718 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012719 val = RLIM_INFINITY;
12720 else {
12721 val = (rlim_t) 0;
12722
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012723 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012724 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012725 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012726 if (val < (rlim_t) 0)
12727 break;
12728 }
12729 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012730 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012731 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012732 }
12733 }
12734 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012735 const char *lname = limits_name;
12736 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012737 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012738 out1fmt("%-20s ", lname);
12739 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012740 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012741 }
12742 return 0;
12743 }
12744
12745 getrlimit(l->cmd, &limit);
12746 if (set) {
12747 if (how & HARD)
12748 limit.rlim_max = val;
12749 if (how & SOFT)
12750 limit.rlim_cur = val;
12751 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012752 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012753 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012754 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012755 }
12756 return 0;
12757}
12758
Eric Andersen90898442003-08-06 11:20:52 +000012759
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012760/* ============ Math support */
12761
Denis Vlasenko131ae172007-02-18 13:00:19 +000012762#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012763
12764/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12765
12766 Permission is hereby granted, free of charge, to any person obtaining
12767 a copy of this software and associated documentation files (the
12768 "Software"), to deal in the Software without restriction, including
12769 without limitation the rights to use, copy, modify, merge, publish,
12770 distribute, sublicense, and/or sell copies of the Software, and to
12771 permit persons to whom the Software is furnished to do so, subject to
12772 the following conditions:
12773
12774 The above copyright notice and this permission notice shall be
12775 included in all copies or substantial portions of the Software.
12776
12777 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12778 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12779 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12780 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12781 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12782 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12783 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12784*/
12785
12786/* This is my infix parser/evaluator. It is optimized for size, intended
12787 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012788 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012789 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012790 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012791 * be that which POSIX specifies for shells. */
12792
12793/* The code uses a simple two-stack algorithm. See
12794 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012795 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012796 * this is based (this code differs in that it applies operators immediately
12797 * to the stack instead of adding them to a queue to end up with an
12798 * expression). */
12799
12800/* To use the routine, call it with an expression string and error return
12801 * pointer */
12802
12803/*
12804 * Aug 24, 2001 Manuel Novoa III
12805 *
12806 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12807 *
12808 * 1) In arith_apply():
12809 * a) Cached values of *numptr and &(numptr[-1]).
12810 * b) Removed redundant test for zero denominator.
12811 *
12812 * 2) In arith():
12813 * a) Eliminated redundant code for processing operator tokens by moving
12814 * to a table-based implementation. Also folded handling of parens
12815 * into the table.
12816 * b) Combined all 3 loops which called arith_apply to reduce generated
12817 * code size at the cost of speed.
12818 *
12819 * 3) The following expressions were treated as valid by the original code:
12820 * 1() , 0! , 1 ( *3 ) .
12821 * These bugs have been fixed by internally enclosing the expression in
12822 * parens and then checking that all binary ops and right parens are
12823 * preceded by a valid expression (NUM_TOKEN).
12824 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012825 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012826 * ctype's isspace() if it is used by another busybox applet or if additional
12827 * whitespace chars should be considered. Look below the "#include"s for a
12828 * precompiler test.
12829 */
12830
12831/*
12832 * Aug 26, 2001 Manuel Novoa III
12833 *
12834 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12835 *
12836 * Merge in Aaron's comments previously posted to the busybox list,
12837 * modified slightly to take account of my changes to the code.
12838 *
12839 */
12840
12841/*
12842 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12843 *
12844 * - allow access to variable,
12845 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12846 * - realize assign syntax (VAR=expr, +=, *= etc)
12847 * - realize exponentiation (** operator)
12848 * - realize comma separated - expr, expr
12849 * - realise ++expr --expr expr++ expr--
12850 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012851 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012852 * - was restored loses XOR operator
12853 * - remove one goto label, added three ;-)
12854 * - protect $((num num)) as true zero expr (Manuel`s error)
12855 * - always use special isspace(), see comment from bash ;-)
12856 */
12857
Eric Andersen90898442003-08-06 11:20:52 +000012858#define arith_isspace(arithval) \
12859 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12860
Eric Andersen90898442003-08-06 11:20:52 +000012861typedef unsigned char operator;
12862
12863/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012864 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012865 * precedence. The ID portion is so that multiple operators can have the
12866 * same precedence, ensuring that the leftmost one is evaluated first.
12867 * Consider * and /. */
12868
12869#define tok_decl(prec,id) (((id)<<5)|(prec))
12870#define PREC(op) ((op) & 0x1F)
12871
12872#define TOK_LPAREN tok_decl(0,0)
12873
12874#define TOK_COMMA tok_decl(1,0)
12875
12876#define TOK_ASSIGN tok_decl(2,0)
12877#define TOK_AND_ASSIGN tok_decl(2,1)
12878#define TOK_OR_ASSIGN tok_decl(2,2)
12879#define TOK_XOR_ASSIGN tok_decl(2,3)
12880#define TOK_PLUS_ASSIGN tok_decl(2,4)
12881#define TOK_MINUS_ASSIGN tok_decl(2,5)
12882#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12883#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12884
12885#define TOK_MUL_ASSIGN tok_decl(3,0)
12886#define TOK_DIV_ASSIGN tok_decl(3,1)
12887#define TOK_REM_ASSIGN tok_decl(3,2)
12888
12889/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012890#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012891
12892/* conditional is right associativity too */
12893#define TOK_CONDITIONAL tok_decl(4,0)
12894#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12895
12896#define TOK_OR tok_decl(5,0)
12897
12898#define TOK_AND tok_decl(6,0)
12899
12900#define TOK_BOR tok_decl(7,0)
12901
12902#define TOK_BXOR tok_decl(8,0)
12903
12904#define TOK_BAND tok_decl(9,0)
12905
12906#define TOK_EQ tok_decl(10,0)
12907#define TOK_NE tok_decl(10,1)
12908
12909#define TOK_LT tok_decl(11,0)
12910#define TOK_GT tok_decl(11,1)
12911#define TOK_GE tok_decl(11,2)
12912#define TOK_LE tok_decl(11,3)
12913
12914#define TOK_LSHIFT tok_decl(12,0)
12915#define TOK_RSHIFT tok_decl(12,1)
12916
12917#define TOK_ADD tok_decl(13,0)
12918#define TOK_SUB tok_decl(13,1)
12919
12920#define TOK_MUL tok_decl(14,0)
12921#define TOK_DIV tok_decl(14,1)
12922#define TOK_REM tok_decl(14,2)
12923
12924/* exponent is right associativity */
12925#define TOK_EXPONENT tok_decl(15,1)
12926
12927/* For now unary operators. */
12928#define UNARYPREC 16
12929#define TOK_BNOT tok_decl(UNARYPREC,0)
12930#define TOK_NOT tok_decl(UNARYPREC,1)
12931
12932#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12933#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12934
12935#define PREC_PRE (UNARYPREC+2)
12936
12937#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12938#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12939
12940#define PREC_POST (UNARYPREC+3)
12941
12942#define TOK_POST_INC tok_decl(PREC_POST, 0)
12943#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12944
12945#define SPEC_PREC (UNARYPREC+4)
12946
12947#define TOK_NUM tok_decl(SPEC_PREC, 0)
12948#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12949
12950#define NUMPTR (*numstackptr)
12951
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012952static int
12953tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012954{
12955 operator prec = PREC(op);
12956
12957 convert_prec_is_assing(prec);
12958 return (prec == PREC(TOK_ASSIGN) ||
12959 prec == PREC_PRE || prec == PREC_POST);
12960}
12961
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012962static int
12963is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012964{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012965 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12966 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012967}
12968
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012969typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000012970 arith_t val;
12971 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012972 char contidional_second_val_initialized;
12973 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012974 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012975} v_n_t;
12976
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012977typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000012978 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012979 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000012980} chk_var_recursive_looped_t;
12981
12982static chk_var_recursive_looped_t *prev_chk_var_recursive;
12983
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012984static int
12985arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012986{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012987 if (t->var) {
12988 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012989
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012990 if (p) {
12991 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012992
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012993 /* recursive try as expression */
12994 chk_var_recursive_looped_t *cur;
12995 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012996
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012997 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12998 if (strcmp(cur->var, t->var) == 0) {
12999 /* expression recursion loop detected */
13000 return -5;
13001 }
13002 }
13003 /* save current lookuped var name */
13004 cur = prev_chk_var_recursive;
13005 cur_save.var = t->var;
13006 cur_save.next = cur;
13007 prev_chk_var_recursive = &cur_save;
13008
13009 t->val = arith (p, &errcode);
13010 /* restore previous ptr after recursiving */
13011 prev_chk_var_recursive = cur;
13012 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013013 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013014 /* allow undefined var as 0 */
13015 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013016 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013018}
13019
13020/* "applying" a token means performing it on the top elements on the integer
13021 * stack. For a unary operator it will only change the top element, but a
13022 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013023static int
13024arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013025{
Eric Andersen90898442003-08-06 11:20:52 +000013026 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013027 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013028 int ret_arith_lookup_val;
13029
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013030 /* There is no operator that can work without arguments */
13031 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013032 numptr_m1 = NUMPTR - 1;
13033
13034 /* check operand is var with noninteger value */
13035 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013036 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013037 return ret_arith_lookup_val;
13038
13039 rez = numptr_m1->val;
13040 if (op == TOK_UMINUS)
13041 rez *= -1;
13042 else if (op == TOK_NOT)
13043 rez = !rez;
13044 else if (op == TOK_BNOT)
13045 rez = ~rez;
13046 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13047 rez++;
13048 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13049 rez--;
13050 else if (op != TOK_UPLUS) {
13051 /* Binary operators */
13052
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013053 /* check and binary operators need two arguments */
13054 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013055
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013056 /* ... and they pop one */
13057 --NUMPTR;
13058 numptr_val = rez;
13059 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013060 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013061 /* protect $((expr1 ? expr2)) without ": expr" */
13062 goto err;
13063 }
13064 rez = numptr_m1->contidional_second_val;
13065 } else if (numptr_m1->contidional_second_val_initialized) {
13066 /* protect $((expr1 : expr2)) without "expr ? " */
13067 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013068 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013069 numptr_m1 = NUMPTR - 1;
13070 if (op != TOK_ASSIGN) {
13071 /* check operand is var with noninteger value for not '=' */
13072 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13073 if (ret_arith_lookup_val)
13074 return ret_arith_lookup_val;
13075 }
13076 if (op == TOK_CONDITIONAL) {
13077 numptr_m1->contidional_second_val = rez;
13078 }
13079 rez = numptr_m1->val;
13080 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013081 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013082 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013083 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013084 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013085 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013086 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013087 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013088 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013089 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013090 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013091 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013092 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013093 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013094 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013095 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013096 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013097 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013098 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013099 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013100 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013101 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013102 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013103 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013104 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013105 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013106 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013107 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013108 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013109 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013110 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013111 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013112 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013113 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013114 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013115 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013116 /* protect $((expr : expr)) without "expr ? " */
13117 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013118 }
13119 numptr_m1->contidional_second_val_initialized = op;
13120 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013121 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013122 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013123 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013124 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013125 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013126 return -3; /* exponent less than 0 */
13127 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013128 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013129
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013130 if (numptr_val)
13131 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013132 c *= rez;
13133 rez = c;
13134 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013135 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013136 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013137 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013138 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013139 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013140 rez %= numptr_val;
13141 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013142 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013143 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013144
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013145 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013146 /* Hmm, 1=2 ? */
13147 goto err;
13148 }
13149 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013150#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013151 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013152#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013153 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013154#endif
Eric Andersen90898442003-08-06 11:20:52 +000013155 setvar(numptr_m1->var, buf, 0);
13156 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013157 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013158 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013159 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013160 rez++;
13161 }
13162 numptr_m1->val = rez;
13163 /* protect geting var value, is number now */
13164 numptr_m1->var = NULL;
13165 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013166 err:
13167 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013168}
13169
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013170/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013171static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013172 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13173 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13174 '<','<', 0, TOK_LSHIFT,
13175 '>','>', 0, TOK_RSHIFT,
13176 '|','|', 0, TOK_OR,
13177 '&','&', 0, TOK_AND,
13178 '!','=', 0, TOK_NE,
13179 '<','=', 0, TOK_LE,
13180 '>','=', 0, TOK_GE,
13181 '=','=', 0, TOK_EQ,
13182 '|','=', 0, TOK_OR_ASSIGN,
13183 '&','=', 0, TOK_AND_ASSIGN,
13184 '*','=', 0, TOK_MUL_ASSIGN,
13185 '/','=', 0, TOK_DIV_ASSIGN,
13186 '%','=', 0, TOK_REM_ASSIGN,
13187 '+','=', 0, TOK_PLUS_ASSIGN,
13188 '-','=', 0, TOK_MINUS_ASSIGN,
13189 '-','-', 0, TOK_POST_DEC,
13190 '^','=', 0, TOK_XOR_ASSIGN,
13191 '+','+', 0, TOK_POST_INC,
13192 '*','*', 0, TOK_EXPONENT,
13193 '!', 0, TOK_NOT,
13194 '<', 0, TOK_LT,
13195 '>', 0, TOK_GT,
13196 '=', 0, TOK_ASSIGN,
13197 '|', 0, TOK_BOR,
13198 '&', 0, TOK_BAND,
13199 '*', 0, TOK_MUL,
13200 '/', 0, TOK_DIV,
13201 '%', 0, TOK_REM,
13202 '+', 0, TOK_ADD,
13203 '-', 0, TOK_SUB,
13204 '^', 0, TOK_BXOR,
13205 /* uniq */
13206 '~', 0, TOK_BNOT,
13207 ',', 0, TOK_COMMA,
13208 '?', 0, TOK_CONDITIONAL,
13209 ':', 0, TOK_CONDITIONAL_SEP,
13210 ')', 0, TOK_RPAREN,
13211 '(', 0, TOK_LPAREN,
13212 0
13213};
13214/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013215#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013216
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013217static arith_t
13218arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013219{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013220 char arithval; /* Current character under analysis */
13221 operator lasttok, op;
13222 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013223 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013224 const char *p = endexpression;
13225 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013226 v_n_t *numstack, *numstackptr;
13227 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013228
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013229 /* Stack of integers */
13230 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13231 * in any given correct or incorrect expression is left as an exercise to
13232 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013233 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013234 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013235 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013236
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013237 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13238 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013239
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013240 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013241 arithval = *expr;
13242 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013243 if (p == endexpression) {
13244 /* Null expression. */
13245 return 0;
13246 }
13247
13248 /* This is only reached after all tokens have been extracted from the
13249 * input stream. If there are still tokens on the operator stack, they
13250 * are to be applied in order. At the end, there should be a final
13251 * result on the integer stack */
13252
13253 if (expr != endexpression + 1) {
13254 /* If we haven't done so already, */
13255 /* append a closing right paren */
13256 expr = endexpression;
13257 /* and let the loop process it. */
13258 continue;
13259 }
13260 /* At this point, we're done with the expression. */
13261 if (numstackptr != numstack+1) {
13262 /* ... but if there isn't, it's bad */
13263 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013264 *perrcode = -1;
13265 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013266 }
13267 if (numstack->var) {
13268 /* expression is $((var)) only, lookup now */
13269 errcode = arith_lookup_val(numstack);
13270 }
13271 ret:
13272 *perrcode = errcode;
13273 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013274 }
13275
Eric Andersen90898442003-08-06 11:20:52 +000013276 /* Continue processing the expression. */
13277 if (arith_isspace(arithval)) {
13278 /* Skip whitespace */
13279 goto prologue;
13280 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013281 p = endofname(expr);
13282 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013283 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013284
13285 numstackptr->var = alloca(var_name_size);
13286 safe_strncpy(numstackptr->var, expr, var_name_size);
13287 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013288 num:
Eric Andersen90898442003-08-06 11:20:52 +000013289 numstackptr->contidional_second_val_initialized = 0;
13290 numstackptr++;
13291 lasttok = TOK_NUM;
13292 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013293 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013294 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013295 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013296#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013297 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013298#else
13299 numstackptr->val = strtol(expr, (char **) &expr, 0);
13300#endif
Eric Andersen90898442003-08-06 11:20:52 +000013301 goto num;
13302 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013303 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013304 const char *o;
13305
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013306 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013307 /* strange operator not found */
13308 goto err;
13309 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013310 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013311 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013312 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013313 /* found */
13314 expr = o - 1;
13315 break;
13316 }
13317 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013318 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013319 p++;
13320 /* skip zero delim */
13321 p++;
13322 }
13323 op = p[1];
13324
13325 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013326 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13327 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013328
13329 /* Plus and minus are binary (not unary) _only_ if the last
13330 * token was as number, or a right paren (which pretends to be
13331 * a number, since it evaluates to one). Think about it.
13332 * It makes sense. */
13333 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013334 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013335 case TOK_ADD:
13336 op = TOK_UPLUS;
13337 break;
13338 case TOK_SUB:
13339 op = TOK_UMINUS;
13340 break;
13341 case TOK_POST_INC:
13342 op = TOK_PRE_INC;
13343 break;
13344 case TOK_POST_DEC:
13345 op = TOK_PRE_DEC;
13346 break;
Eric Andersen90898442003-08-06 11:20:52 +000013347 }
13348 }
13349 /* We don't want a unary operator to cause recursive descent on the
13350 * stack, because there can be many in a row and it could cause an
13351 * operator to be evaluated before its argument is pushed onto the
13352 * integer stack. */
13353 /* But for binary operators, "apply" everything on the operator
13354 * stack until we find an operator with a lesser priority than the
13355 * one we have just extracted. */
13356 /* Left paren is given the lowest priority so it will never be
13357 * "applied" in this way.
13358 * if associativity is right and priority eq, applied also skip
13359 */
13360 prec = PREC(op);
13361 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13362 /* not left paren or unary */
13363 if (lasttok != TOK_NUM) {
13364 /* binary op must be preceded by a num */
13365 goto err;
13366 }
13367 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013368 if (op == TOK_RPAREN) {
13369 /* The algorithm employed here is simple: while we don't
13370 * hit an open paren nor the bottom of the stack, pop
13371 * tokens and apply them */
13372 if (stackptr[-1] == TOK_LPAREN) {
13373 --stackptr;
13374 /* Any operator directly after a */
13375 lasttok = TOK_NUM;
13376 /* close paren should consider itself binary */
13377 goto prologue;
13378 }
13379 } else {
13380 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013381
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013382 convert_prec_is_assing(prec);
13383 convert_prec_is_assing(prev_prec);
13384 if (prev_prec < prec)
13385 break;
13386 /* check right assoc */
13387 if (prev_prec == prec && is_right_associativity(prec))
13388 break;
13389 }
13390 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13391 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013392 }
13393 if (op == TOK_RPAREN) {
13394 goto err;
13395 }
13396 }
13397
13398 /* Push this operator to the stack and remember it. */
13399 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013400 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013401 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013402 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013403}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013404#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013405
13406
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013407/* ============ main() and helpers */
13408
13409/*
13410 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013411 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013412static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013413static void
13414exitshell(void)
13415{
13416 struct jmploc loc;
13417 char *p;
13418 int status;
13419
13420 status = exitstatus;
13421 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13422 if (setjmp(loc.loc)) {
13423 if (exception == EXEXIT)
13424/* dash bug: it just does _exit(exitstatus) here
13425 * but we have to do setjobctl(0) first!
13426 * (bug is still not fixed in dash-0.5.3 - if you run dash
13427 * under Midnight Commander, on exit from dash MC is backgrounded) */
13428 status = exitstatus;
13429 goto out;
13430 }
13431 exception_handler = &loc;
13432 p = trap[0];
13433 if (p) {
13434 trap[0] = NULL;
13435 evalstring(p, 0);
13436 }
13437 flush_stdout_stderr();
13438 out:
13439 setjobctl(0);
13440 _exit(status);
13441 /* NOTREACHED */
13442}
13443
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013444static void
13445init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013446{
13447 /* from input.c: */
13448 basepf.nextc = basepf.buf = basebuf;
13449
13450 /* from trap.c: */
13451 signal(SIGCHLD, SIG_DFL);
13452
13453 /* from var.c: */
13454 {
13455 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013456 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013457 const char *p;
13458 struct stat st1, st2;
13459
13460 initvar();
13461 for (envp = environ; envp && *envp; envp++) {
13462 if (strchr(*envp, '=')) {
13463 setvareq(*envp, VEXPORT|VTEXTFIXED);
13464 }
13465 }
13466
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013467 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013468 setvar("PPID", ppid, 0);
13469
13470 p = lookupvar("PWD");
13471 if (p)
13472 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13473 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13474 p = '\0';
13475 setpwd(p, 0);
13476 }
13477}
13478
13479/*
13480 * Process the shell command line arguments.
13481 */
13482static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013483procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013484{
13485 int i;
13486 const char *xminusc;
13487 char **xargv;
13488
13489 xargv = argv;
13490 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013491 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013492 xargv++;
13493 for (i = 0; i < NOPTS; i++)
13494 optlist[i] = 2;
13495 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013496 if (options(1)) {
13497 /* it already printed err message */
13498 raise_exception(EXERROR);
13499 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013500 xargv = argptr;
13501 xminusc = minusc;
13502 if (*xargv == NULL) {
13503 if (xminusc)
13504 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13505 sflag = 1;
13506 }
13507 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13508 iflag = 1;
13509 if (mflag == 2)
13510 mflag = iflag;
13511 for (i = 0; i < NOPTS; i++)
13512 if (optlist[i] == 2)
13513 optlist[i] = 0;
13514#if DEBUG == 2
13515 debug = 1;
13516#endif
13517 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13518 if (xminusc) {
13519 minusc = *xargv++;
13520 if (*xargv)
13521 goto setarg0;
13522 } else if (!sflag) {
13523 setinputfile(*xargv, 0);
13524 setarg0:
13525 arg0 = *xargv++;
13526 commandname = arg0;
13527 }
13528
13529 shellparam.p = xargv;
13530#if ENABLE_ASH_GETOPTS
13531 shellparam.optind = 1;
13532 shellparam.optoff = -1;
13533#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013534 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013535 while (*xargv) {
13536 shellparam.nparam++;
13537 xargv++;
13538 }
13539 optschanged();
13540}
13541
13542/*
13543 * Read /etc/profile or .profile.
13544 */
13545static void
13546read_profile(const char *name)
13547{
13548 int skip;
13549
13550 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13551 return;
13552 skip = cmdloop(0);
13553 popfile();
13554 if (skip)
13555 exitshell();
13556}
13557
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013558/*
13559 * This routine is called when an error or an interrupt occurs in an
13560 * interactive shell and control is returned to the main command loop.
13561 */
13562static void
13563reset(void)
13564{
13565 /* from eval.c: */
13566 evalskip = 0;
13567 loopnest = 0;
13568 /* from input.c: */
13569 parselleft = parsenleft = 0; /* clear input buffer */
13570 popallfiles();
13571 /* from parser.c: */
13572 tokpushback = 0;
13573 checkkwd = 0;
13574 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013575 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013576}
13577
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013578#if PROFILE
13579static short profile_buf[16384];
13580extern int etext();
13581#endif
13582
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013583/*
13584 * Main routine. We initialize things, parse the arguments, execute
13585 * profiles if we're a login shell, and then call cmdloop to execute
13586 * commands. The setjmp call sets up the location to jump to when an
13587 * exception occurs. When an exception occurs the variable "state"
13588 * is used to figure out how far we had gotten.
13589 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013590int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013591int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013592{
13593 char *shinit;
13594 volatile int state;
13595 struct jmploc jmploc;
13596 struct stackmark smark;
13597
Denis Vlasenko01631112007-12-16 17:20:38 +000013598 /* Initialize global data */
13599 INIT_G_misc();
13600 INIT_G_memstack();
13601 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013602#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013603 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013604#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013605 INIT_G_cmdtable();
13606
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013607#if PROFILE
13608 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13609#endif
13610
13611#if ENABLE_FEATURE_EDITING
13612 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13613#endif
13614 state = 0;
13615 if (setjmp(jmploc.loc)) {
13616 int e;
13617 int s;
13618
13619 reset();
13620
13621 e = exception;
13622 if (e == EXERROR)
13623 exitstatus = 2;
13624 s = state;
13625 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13626 exitshell();
13627
13628 if (e == EXINT) {
13629 outcslow('\n', stderr);
13630 }
13631 popstackmark(&smark);
13632 FORCE_INT_ON; /* enable interrupts */
13633 if (s == 1)
13634 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013635 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013636 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013637 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013638 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013639 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013640 }
13641 exception_handler = &jmploc;
13642#if DEBUG
13643 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013644 trace_puts("Shell args: ");
13645 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013646#endif
13647 rootpid = getpid();
13648
13649#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013650 /* Can use monotonic_ns() for better randomness but for now it is
13651 * not used anywhere else in busybox... so avoid bloat */
13652 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013653#endif
13654 init();
13655 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013656 procargs(argv);
13657
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013658#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13659 if (iflag) {
13660 const char *hp = lookupvar("HISTFILE");
13661
13662 if (hp == NULL) {
13663 hp = lookupvar("HOME");
13664 if (hp != NULL) {
13665 char *defhp = concat_path_file(hp, ".ash_history");
13666 setvar("HISTFILE", defhp, 0);
13667 free(defhp);
13668 }
13669 }
13670 }
13671#endif
13672 if (argv[0] && argv[0][0] == '-')
13673 isloginsh = 1;
13674 if (isloginsh) {
13675 state = 1;
13676 read_profile("/etc/profile");
13677 state1:
13678 state = 2;
13679 read_profile(".profile");
13680 }
13681 state2:
13682 state = 3;
13683 if (
13684#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013685 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013686#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013687 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013688 ) {
13689 shinit = lookupvar("ENV");
13690 if (shinit != NULL && *shinit != '\0') {
13691 read_profile(shinit);
13692 }
13693 }
13694 state3:
13695 state = 4;
13696 if (minusc)
13697 evalstring(minusc, 0);
13698
13699 if (sflag || minusc == NULL) {
13700#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013701 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013702 const char *hp = lookupvar("HISTFILE");
13703
13704 if (hp != NULL)
13705 line_input_state->hist_file = hp;
13706 }
13707#endif
13708 state4: /* XXX ??? - why isn't this before the "if" statement */
13709 cmdloop(1);
13710 }
13711#if PROFILE
13712 monitor(0);
13713#endif
13714#ifdef GPROF
13715 {
13716 extern void _mcleanup(void);
13717 _mcleanup();
13718 }
13719#endif
13720 exitshell();
13721 /* NOTREACHED */
13722}
13723
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013724#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013725const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013726int main(int argc, char **argv)
13727{
13728 return ash_main(argc, argv);
13729}
13730#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013731
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013732
Eric Andersendf82f612001-06-28 07:46:40 +000013733/*-
13734 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013735 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013736 *
13737 * This code is derived from software contributed to Berkeley by
13738 * Kenneth Almquist.
13739 *
13740 * Redistribution and use in source and binary forms, with or without
13741 * modification, are permitted provided that the following conditions
13742 * are met:
13743 * 1. Redistributions of source code must retain the above copyright
13744 * notice, this list of conditions and the following disclaimer.
13745 * 2. Redistributions in binary form must reproduce the above copyright
13746 * notice, this list of conditions and the following disclaimer in the
13747 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013748 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013749 * may be used to endorse or promote products derived from this software
13750 * without specific prior written permission.
13751 *
13752 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13753 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13754 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13755 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13756 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13757 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13758 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13759 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13760 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13761 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13762 * SUCH DAMAGE.
13763 */