blob: 29d8713b7c803e93682b207de272aa98cae69061 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersen5bb16772001-09-06 18:00:41 +000045#define IFS_BROKEN
Eric Andersenc470f442003-07-28 09:56:35 +000046#define PROFILE 0
Denis Vlasenko131ae172007-02-18 13:00:19 +000047#if ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048#define JOBS 1
49#else
Denis Vlasenko666da5e2006-12-26 18:17:42 +000050#define JOBS 0
Eric Andersenc470f442003-07-28 09:56:35 +000051#endif
52
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#if DEBUG
54#define _GNU_SOURCE
55#endif
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000056#include "busybox.h" /* for struct bb_applet */
Denis Vlasenkob012b102007-02-19 22:43:01 +000057#include <paths.h>
58#include <setjmp.h>
59#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000060#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000061#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000062#endif
Denis Vlasenko5b340832007-05-17 23:02:14 +000063extern char **environ;
Eric Andersencb57d552001-06-28 07:25:16 +000064
Denis Vlasenkob012b102007-02-19 22:43:01 +000065#if defined(__uClinux__)
66#error "Do not even bother, ash will not run on uClinux"
67#endif
68
Denis Vlasenkob012b102007-02-19 22:43:01 +000069
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000070/* ============ Misc helpers */
71
72#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
73
74/* C99 say: "char" declaration may be signed or unsigned default */
75#define signed_char2int(sc) ((int)((signed char)sc))
76
77
Denis Vlasenkob012b102007-02-19 22:43:01 +000078/* ============ Shell options */
79
80static const char *const optletters_optnames[] = {
81 "e" "errexit",
82 "f" "noglob",
83 "I" "ignoreeof",
84 "i" "interactive",
85 "m" "monitor",
86 "n" "noexec",
87 "s" "stdin",
88 "x" "xtrace",
89 "v" "verbose",
90 "C" "noclobber",
91 "a" "allexport",
92 "b" "notify",
93 "u" "nounset",
94 "\0" "vi",
95#if DEBUG
96 "\0" "nolog",
97 "\0" "debug",
98#endif
99};
100
101#define optletters(n) optletters_optnames[(n)][0]
102#define optnames(n) (&optletters_optnames[(n)][1])
103
104#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
105
106static char optlist[NOPTS];
107
108#define eflag optlist[0]
109#define fflag optlist[1]
110#define Iflag optlist[2]
111#define iflag optlist[3]
112#define mflag optlist[4]
113#define nflag optlist[5]
114#define sflag optlist[6]
115#define xflag optlist[7]
116#define vflag optlist[8]
117#define Cflag optlist[9]
118#define aflag optlist[10]
119#define bflag optlist[11]
120#define uflag optlist[12]
121#define viflag optlist[13]
122#if DEBUG
123#define nolog optlist[14]
124#define debug optlist[15]
125#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000126
127
Denis Vlasenkob012b102007-02-19 22:43:01 +0000128/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000129
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000130static char nullstr[1]; /* zero length string */
131static const char homestr[] = "HOME";
132static const char snlfmt[] = "%s\n";
133static const char illnum[] = "Illegal number: %s";
134
Denis Vlasenkocc571512007-02-23 21:10:35 +0000135static char *minusc; /* argument to -c option */
136
Denis Vlasenkoa624c112007-02-19 22:45:43 +0000137static int isloginsh;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000138/* pid of main shell */
139static int rootpid;
140/* shell level: 0 for the main shell, 1 for its children, and so on */
141static int shlvl;
142#define rootshell (!shlvl)
143/* trap handler commands */
144static char *trap[NSIG];
145/* current value of signal */
146static char sigmode[NSIG - 1];
147/* indicates specified signal received */
148static char gotsig[NSIG - 1];
149static char *arg0; /* value of $0 */
Eric Andersenc470f442003-07-28 09:56:35 +0000150
151
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000152/* ============ Interrupts / exceptions */
153
154/*
Eric Andersenc470f442003-07-28 09:56:35 +0000155 * We enclose jmp_buf in a structure so that we can declare pointers to
156 * jump locations. The global variable handler contains the location to
157 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000158 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000159 * exception handlers, the user should save the value of handler on entry
160 * to an inner scope, set handler to point to a jmploc structure for the
161 * inner scope, and restore handler on exit from the scope.
162 */
Eric Andersenc470f442003-07-28 09:56:35 +0000163struct jmploc {
164 jmp_buf loc;
165};
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000166static struct jmploc *exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +0000167static int exception;
Eric Andersenc470f442003-07-28 09:56:35 +0000168/* exceptions */
169#define EXINT 0 /* SIGINT received */
170#define EXERROR 1 /* a generic error */
171#define EXSHELLPROC 2 /* execute a shell procedure */
172#define EXEXEC 3 /* command execution failed */
173#define EXEXIT 4 /* exit the shell */
174#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000175static volatile int suppressint;
176static volatile sig_atomic_t intpending;
Eric Andersenc470f442003-07-28 09:56:35 +0000177/* do we generate EXSIG events */
178static int exsig;
179/* last pending signal */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000180static volatile sig_atomic_t pendingsig;
Eric Andersen2870d962001-07-02 17:27:21 +0000181
182/*
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000183 * Sigmode records the current value of the signal handlers for the various
184 * modes. A value of zero means that the current handler is not known.
185 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
186 */
187
188#define S_DFL 1 /* default signal handling (SIG_DFL) */
189#define S_CATCH 2 /* signal is caught */
190#define S_IGN 3 /* signal is ignored (SIG_IGN) */
191#define S_HARD_IGN 4 /* signal is ignored permenantly */
192#define S_RESET 5 /* temporary - to reset a hard ignored sig */
193
194/*
Eric Andersen2870d962001-07-02 17:27:21 +0000195 * These macros allow the user to suspend the handling of interrupt signals
196 * over a period of time. This is similar to SIGHOLD to or sigblock, but
197 * much more efficient and portable. (But hacking the kernel is so much
198 * more fun than worrying about efficiency and portability. :-))
199 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000200#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000201 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000202 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000203 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000204 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000205
206/*
207 * Called to raise an exception. Since C doesn't include exceptions, we
208 * just do a longjmp to the exception handler. The type of exception is
209 * stored in the global variable "exception".
210 */
211static void raise_exception(int) ATTRIBUTE_NORETURN;
212static void
213raise_exception(int e)
214{
215#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000216 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000217 abort();
218#endif
219 INT_OFF;
220 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000221 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000222}
223
224/*
225 * Called from trap.c when a SIGINT is received. (If the user specifies
226 * that SIGINT is to be trapped or ignored using the trap builtin, then
227 * this routine is not called.) Suppressint is nonzero when interrupts
228 * are held using the INT_OFF macro. (The test for iflag is just
229 * defensive programming.)
230 */
231static void raise_interrupt(void) ATTRIBUTE_NORETURN;
232static void
233raise_interrupt(void)
234{
235 int i;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000236 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000237
238 intpending = 0;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000239 /* Signal is not automatically re-enabled after it is raised,
240 * do it ourself */
241 sigemptyset(&mask);
242 sigprocmask(SIG_SETMASK, &mask, 0);
243 /* pendingsig = 0; - now done in onsig() */
244
Denis Vlasenkob012b102007-02-19 22:43:01 +0000245 i = EXSIG;
246 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
247 if (!(rootshell && iflag)) {
248 signal(SIGINT, SIG_DFL);
249 raise(SIGINT);
250 }
251 i = EXINT;
252 }
253 raise_exception(i);
254 /* NOTREACHED */
255}
256
257#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000258static void
259int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000260{
261 if (--suppressint == 0 && intpending) {
262 raise_interrupt();
263 }
264}
265#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000266static void
267force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000268{
269 suppressint = 0;
270 if (intpending)
271 raise_interrupt();
272}
273#define FORCE_INT_ON force_int_on()
274#else
275#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000276 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000277 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000278 if (--suppressint == 0 && intpending) \
279 raise_interrupt(); \
280 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000281#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000282 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000283 xbarrier(); \
284 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000285 if (intpending) \
286 raise_interrupt(); \
287 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000288#endif /* ASH_OPTIMIZE_FOR_SIZE */
289
290#define SAVE_INT(v) ((v) = suppressint)
291
292#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000293 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000294 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000295 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000296 if (suppressint == 0 && intpending) \
297 raise_interrupt(); \
298 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000299
300#define EXSIGON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000301 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000302 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000303 xbarrier(); \
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000304 if (pendingsig) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000305 raise_exception(EXSIG); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000306 } while (0)
Eric Andersenc470f442003-07-28 09:56:35 +0000307/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000308
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000309/*
310 * Ignore a signal. Only one usage site - in forkchild()
311 */
312static void
313ignoresig(int signo)
314{
315 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
316 signal(signo, SIG_IGN);
317 }
318 sigmode[signo - 1] = S_HARD_IGN;
319}
320
321/*
322 * Signal handler. Only one usage site - in setsignal()
323 */
324static void
325onsig(int signo)
326{
327 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000328 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000329
330 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000331 if (!suppressint) {
332 pendingsig = 0;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000333 raise_interrupt();
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000334 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000335 intpending = 1;
336 }
337}
338
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000339
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000340/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000341
Eric Andersenc470f442003-07-28 09:56:35 +0000342static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000343outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000344{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000345 INT_OFF;
346 fputs(p, file);
347 INT_ON;
348}
349
350static void
351flush_stdout_stderr(void)
352{
353 INT_OFF;
354 fflush(stdout);
355 fflush(stderr);
356 INT_ON;
357}
358
359static void
360flush_stderr(void)
361{
362 INT_OFF;
363 fflush(stderr);
364 INT_ON;
365}
366
367static void
368outcslow(int c, FILE *dest)
369{
370 INT_OFF;
371 putc(c, dest);
372 fflush(dest);
373 INT_ON;
374}
375
376static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
377static int
378out1fmt(const char *fmt, ...)
379{
380 va_list ap;
381 int r;
382
383 INT_OFF;
384 va_start(ap, fmt);
385 r = vprintf(fmt, ap);
386 va_end(ap);
387 INT_ON;
388 return r;
389}
390
391static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
392static int
393fmtstr(char *outbuf, size_t length, const char *fmt, ...)
394{
395 va_list ap;
396 int ret;
397
398 va_start(ap, fmt);
399 INT_OFF;
400 ret = vsnprintf(outbuf, length, fmt, ap);
401 va_end(ap);
402 INT_ON;
403 return ret;
404}
405
406static void
407out1str(const char *p)
408{
409 outstr(p, stdout);
410}
411
412static void
413out2str(const char *p)
414{
415 outstr(p, stderr);
416 flush_stderr();
417}
418
419
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000420/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000421
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000422/* control characters in argument strings */
423#define CTLESC '\201' /* escape next character */
424#define CTLVAR '\202' /* variable defn */
425#define CTLENDVAR '\203'
426#define CTLBACKQ '\204'
427#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
428/* CTLBACKQ | CTLQUOTE == '\205' */
429#define CTLARI '\206' /* arithmetic expression */
430#define CTLENDARI '\207'
431#define CTLQUOTEMARK '\210'
432
433/* variable substitution byte (follows CTLVAR) */
434#define VSTYPE 0x0f /* type of variable substitution */
435#define VSNUL 0x10 /* colon--treat the empty string as unset */
436#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
437
438/* values of VSTYPE field */
439#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
440#define VSMINUS 0x2 /* ${var-text} */
441#define VSPLUS 0x3 /* ${var+text} */
442#define VSQUESTION 0x4 /* ${var?message} */
443#define VSASSIGN 0x5 /* ${var=text} */
444#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
445#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
446#define VSTRIMLEFT 0x8 /* ${var#pattern} */
447#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
448#define VSLENGTH 0xa /* ${#var} */
449
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000450static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
451
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000452#define NCMD 0
453#define NPIPE 1
454#define NREDIR 2
455#define NBACKGND 3
456#define NSUBSHELL 4
457#define NAND 5
458#define NOR 6
459#define NSEMI 7
460#define NIF 8
461#define NWHILE 9
462#define NUNTIL 10
463#define NFOR 11
464#define NCASE 12
465#define NCLIST 13
466#define NDEFUN 14
467#define NARG 15
468#define NTO 16
469#define NCLOBBER 17
470#define NFROM 18
471#define NFROMTO 19
472#define NAPPEND 20
473#define NTOFD 21
474#define NFROMFD 22
475#define NHERE 23
476#define NXHERE 24
477#define NNOT 25
478
479union node;
480
481struct ncmd {
482 int type;
483 union node *assign;
484 union node *args;
485 union node *redirect;
486};
487
488struct npipe {
489 int type;
490 int backgnd;
491 struct nodelist *cmdlist;
492};
493
494struct nredir {
495 int type;
496 union node *n;
497 union node *redirect;
498};
499
500struct nbinary {
501 int type;
502 union node *ch1;
503 union node *ch2;
504};
505
506struct nif {
507 int type;
508 union node *test;
509 union node *ifpart;
510 union node *elsepart;
511};
512
513struct nfor {
514 int type;
515 union node *args;
516 union node *body;
517 char *var;
518};
519
520struct ncase {
521 int type;
522 union node *expr;
523 union node *cases;
524};
525
526struct nclist {
527 int type;
528 union node *next;
529 union node *pattern;
530 union node *body;
531};
532
533struct narg {
534 int type;
535 union node *next;
536 char *text;
537 struct nodelist *backquote;
538};
539
540struct nfile {
541 int type;
542 union node *next;
543 int fd;
544 union node *fname;
545 char *expfname;
546};
547
548struct ndup {
549 int type;
550 union node *next;
551 int fd;
552 int dupfd;
553 union node *vname;
554};
555
556struct nhere {
557 int type;
558 union node *next;
559 int fd;
560 union node *doc;
561};
562
563struct nnot {
564 int type;
565 union node *com;
566};
567
568union node {
569 int type;
570 struct ncmd ncmd;
571 struct npipe npipe;
572 struct nredir nredir;
573 struct nbinary nbinary;
574 struct nif nif;
575 struct nfor nfor;
576 struct ncase ncase;
577 struct nclist nclist;
578 struct narg narg;
579 struct nfile nfile;
580 struct ndup ndup;
581 struct nhere nhere;
582 struct nnot nnot;
583};
584
585struct nodelist {
586 struct nodelist *next;
587 union node *n;
588};
589
590struct funcnode {
591 int count;
592 union node n;
593};
594
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000595/*
596 * Free a parse tree.
597 */
598static void
599freefunc(struct funcnode *f)
600{
601 if (f && --f->count < 0)
602 free(f);
603}
604
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000605
606/* ============ Debugging output */
607
608#if DEBUG
609
610static FILE *tracefile;
611
612static void
613trace_printf(const char *fmt, ...)
614{
615 va_list va;
616
617 if (debug != 1)
618 return;
619 va_start(va, fmt);
620 vfprintf(tracefile, fmt, va);
621 va_end(va);
622}
623
624static void
625trace_vprintf(const char *fmt, va_list va)
626{
627 if (debug != 1)
628 return;
629 vfprintf(tracefile, fmt, va);
630}
631
632static void
633trace_puts(const char *s)
634{
635 if (debug != 1)
636 return;
637 fputs(s, tracefile);
638}
639
640static void
641trace_puts_quoted(char *s)
642{
643 char *p;
644 char c;
645
646 if (debug != 1)
647 return;
648 putc('"', tracefile);
649 for (p = s; *p; p++) {
650 switch (*p) {
651 case '\n': c = 'n'; goto backslash;
652 case '\t': c = 't'; goto backslash;
653 case '\r': c = 'r'; goto backslash;
654 case '"': c = '"'; goto backslash;
655 case '\\': c = '\\'; goto backslash;
656 case CTLESC: c = 'e'; goto backslash;
657 case CTLVAR: c = 'v'; goto backslash;
658 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
659 case CTLBACKQ: c = 'q'; goto backslash;
660 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
661 backslash:
662 putc('\\', tracefile);
663 putc(c, tracefile);
664 break;
665 default:
666 if (*p >= ' ' && *p <= '~')
667 putc(*p, tracefile);
668 else {
669 putc('\\', tracefile);
670 putc(*p >> 6 & 03, tracefile);
671 putc(*p >> 3 & 07, tracefile);
672 putc(*p & 07, tracefile);
673 }
674 break;
675 }
676 }
677 putc('"', tracefile);
678}
679
680static void
681trace_puts_args(char **ap)
682{
683 if (debug != 1)
684 return;
685 if (!*ap)
686 return;
687 while (1) {
688 trace_puts_quoted(*ap);
689 if (!*++ap) {
690 putc('\n', tracefile);
691 break;
692 }
693 putc(' ', tracefile);
694 }
695}
696
697static void
698opentrace(void)
699{
700 char s[100];
701#ifdef O_APPEND
702 int flags;
703#endif
704
705 if (debug != 1) {
706 if (tracefile)
707 fflush(tracefile);
708 /* leave open because libedit might be using it */
709 return;
710 }
711 strcpy(s, "./trace");
712 if (tracefile) {
713 if (!freopen(s, "a", tracefile)) {
714 fprintf(stderr, "Can't re-open %s\n", s);
715 debug = 0;
716 return;
717 }
718 } else {
719 tracefile = fopen(s, "a");
720 if (tracefile == NULL) {
721 fprintf(stderr, "Can't open %s\n", s);
722 debug = 0;
723 return;
724 }
725 }
726#ifdef O_APPEND
727 flags = fcntl(fileno(tracefile), F_GETFL, 0);
728 if (flags >= 0)
729 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
730#endif
731 setlinebuf(tracefile);
732 fputs("\nTracing started.\n", tracefile);
733}
734
735static void
736indent(int amount, char *pfx, FILE *fp)
737{
738 int i;
739
740 for (i = 0; i < amount; i++) {
741 if (pfx && i == amount - 1)
742 fputs(pfx, fp);
743 putc('\t', fp);
744 }
745}
746
747/* little circular references here... */
748static void shtree(union node *n, int ind, char *pfx, FILE *fp);
749
750static void
751sharg(union node *arg, FILE *fp)
752{
753 char *p;
754 struct nodelist *bqlist;
755 int subtype;
756
757 if (arg->type != NARG) {
758 out1fmt("<node type %d>\n", arg->type);
759 abort();
760 }
761 bqlist = arg->narg.backquote;
762 for (p = arg->narg.text; *p; p++) {
763 switch (*p) {
764 case CTLESC:
765 putc(*++p, fp);
766 break;
767 case CTLVAR:
768 putc('$', fp);
769 putc('{', fp);
770 subtype = *++p;
771 if (subtype == VSLENGTH)
772 putc('#', fp);
773
774 while (*p != '=')
775 putc(*p++, fp);
776
777 if (subtype & VSNUL)
778 putc(':', fp);
779
780 switch (subtype & VSTYPE) {
781 case VSNORMAL:
782 putc('}', fp);
783 break;
784 case VSMINUS:
785 putc('-', fp);
786 break;
787 case VSPLUS:
788 putc('+', fp);
789 break;
790 case VSQUESTION:
791 putc('?', fp);
792 break;
793 case VSASSIGN:
794 putc('=', fp);
795 break;
796 case VSTRIMLEFT:
797 putc('#', fp);
798 break;
799 case VSTRIMLEFTMAX:
800 putc('#', fp);
801 putc('#', fp);
802 break;
803 case VSTRIMRIGHT:
804 putc('%', fp);
805 break;
806 case VSTRIMRIGHTMAX:
807 putc('%', fp);
808 putc('%', fp);
809 break;
810 case VSLENGTH:
811 break;
812 default:
813 out1fmt("<subtype %d>", subtype);
814 }
815 break;
816 case CTLENDVAR:
817 putc('}', fp);
818 break;
819 case CTLBACKQ:
820 case CTLBACKQ|CTLQUOTE:
821 putc('$', fp);
822 putc('(', fp);
823 shtree(bqlist->n, -1, NULL, fp);
824 putc(')', fp);
825 break;
826 default:
827 putc(*p, fp);
828 break;
829 }
830 }
831}
832
833static void
834shcmd(union node *cmd, FILE *fp)
835{
836 union node *np;
837 int first;
838 const char *s;
839 int dftfd;
840
841 first = 1;
842 for (np = cmd->ncmd.args; np; np = np->narg.next) {
843 if (! first)
844 putchar(' ');
845 sharg(np, fp);
846 first = 0;
847 }
848 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
849 if (! first)
850 putchar(' ');
851 switch (np->nfile.type) {
852 case NTO: s = ">"; dftfd = 1; break;
853 case NCLOBBER: s = ">|"; dftfd = 1; break;
854 case NAPPEND: s = ">>"; dftfd = 1; break;
855 case NTOFD: s = ">&"; dftfd = 1; break;
856 case NFROM: s = "<"; dftfd = 0; break;
857 case NFROMFD: s = "<&"; dftfd = 0; break;
858 case NFROMTO: s = "<>"; dftfd = 0; break;
859 default: s = "*error*"; dftfd = 0; break;
860 }
861 if (np->nfile.fd != dftfd)
862 fprintf(fp, "%d", np->nfile.fd);
863 fputs(s, fp);
864 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
865 fprintf(fp, "%d", np->ndup.dupfd);
866 } else {
867 sharg(np->nfile.fname, fp);
868 }
869 first = 0;
870 }
871}
872
873static void
874shtree(union node *n, int ind, char *pfx, FILE *fp)
875{
876 struct nodelist *lp;
877 const char *s;
878
879 if (n == NULL)
880 return;
881
882 indent(ind, pfx, fp);
883 switch (n->type) {
884 case NSEMI:
885 s = "; ";
886 goto binop;
887 case NAND:
888 s = " && ";
889 goto binop;
890 case NOR:
891 s = " || ";
892 binop:
893 shtree(n->nbinary.ch1, ind, NULL, fp);
894 /* if (ind < 0) */
895 fputs(s, fp);
896 shtree(n->nbinary.ch2, ind, NULL, fp);
897 break;
898 case NCMD:
899 shcmd(n, fp);
900 if (ind >= 0)
901 putc('\n', fp);
902 break;
903 case NPIPE:
904 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
905 shcmd(lp->n, fp);
906 if (lp->next)
907 fputs(" | ", fp);
908 }
909 if (n->npipe.backgnd)
910 fputs(" &", fp);
911 if (ind >= 0)
912 putc('\n', fp);
913 break;
914 default:
915 fprintf(fp, "<node type %d>", n->type);
916 if (ind >= 0)
917 putc('\n', fp);
918 break;
919 }
920}
921
922static void
923showtree(union node *n)
924{
925 trace_puts("showtree called\n");
926 shtree(n, 1, NULL, stdout);
927}
928
929#define TRACE(param) trace_printf param
930#define TRACEV(param) trace_vprintf param
931
932#else
933
934#define TRACE(param)
935#define TRACEV(param)
936
937#endif /* DEBUG */
938
939
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000940/* ============ Parser data */
941
942/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000943 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
944 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000945struct strlist {
946 struct strlist *next;
947 char *text;
948};
949
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000950#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000951struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000952#endif
953
Denis Vlasenkob012b102007-02-19 22:43:01 +0000954struct strpush {
955 struct strpush *prev; /* preceding string on stack */
956 char *prevstring;
957 int prevnleft;
958#if ENABLE_ASH_ALIAS
959 struct alias *ap; /* if push was associated with an alias */
960#endif
961 char *string; /* remember the string since it may change */
962};
963
964struct parsefile {
965 struct parsefile *prev; /* preceding file on stack */
966 int linno; /* current line */
967 int fd; /* file descriptor (or -1 if string) */
968 int nleft; /* number of chars left in this line */
969 int lleft; /* number of chars left in this buffer */
970 char *nextc; /* next char in buffer */
971 char *buf; /* input buffer */
972 struct strpush *strpush; /* for pushing strings at this level */
973 struct strpush basestrpush; /* so pushing one is fast */
974};
975
976static struct parsefile basepf; /* top level input file */
977static struct parsefile *parsefile = &basepf; /* current input file */
978static int startlinno; /* line # where last token started */
979static char *commandname; /* currently executing command */
980static struct strlist *cmdenviron; /* environment for builtin command */
981static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000982
983
984/* ============ Message printing */
985
986static void
987ash_vmsg(const char *msg, va_list ap)
988{
989 fprintf(stderr, "%s: ", arg0);
990 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +0000991 if (strcmp(arg0, commandname))
992 fprintf(stderr, "%s: ", commandname);
993 if (!iflag || parsefile->fd)
994 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +0000995 }
Denis Vlasenkob012b102007-02-19 22:43:01 +0000996 vfprintf(stderr, msg, ap);
997 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +0000998}
Denis Vlasenkob012b102007-02-19 22:43:01 +0000999
1000/*
1001 * Exverror is called to raise the error exception. If the second argument
1002 * is not NULL then error prints an error message using printf style
1003 * formatting. It then raises the error exception.
1004 */
1005static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1006static void
1007ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001008{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001009#if DEBUG
1010 if (msg) {
1011 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1012 TRACEV((msg, ap));
1013 TRACE(("\") pid=%d\n", getpid()));
1014 } else
1015 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1016 if (msg)
1017#endif
1018 ash_vmsg(msg, ap);
1019
1020 flush_stdout_stderr();
1021 raise_exception(cond);
1022 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001023}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001024
1025static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1026static void
1027ash_msg_and_raise_error(const char *msg, ...)
1028{
1029 va_list ap;
1030
1031 va_start(ap, msg);
1032 ash_vmsg_and_raise(EXERROR, msg, ap);
1033 /* NOTREACHED */
1034 va_end(ap);
1035}
1036
1037static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1038static void
1039ash_msg_and_raise(int cond, const char *msg, ...)
1040{
1041 va_list ap;
1042
1043 va_start(ap, msg);
1044 ash_vmsg_and_raise(cond, msg, ap);
1045 /* NOTREACHED */
1046 va_end(ap);
1047}
1048
1049/*
1050 * error/warning routines for external builtins
1051 */
1052static void
1053ash_msg(const char *fmt, ...)
1054{
1055 va_list ap;
1056
1057 va_start(ap, fmt);
1058 ash_vmsg(fmt, ap);
1059 va_end(ap);
1060}
1061
1062/*
1063 * Return a string describing an error. The returned string may be a
1064 * pointer to a static buffer that will be overwritten on the next call.
1065 * Action describes the operation that got the error.
1066 */
1067static const char *
1068errmsg(int e, const char *em)
1069{
1070 if (e == ENOENT || e == ENOTDIR) {
1071 return em;
1072 }
1073 return strerror(e);
1074}
1075
1076
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001077/* ============ Memory allocation */
1078
1079/*
1080 * It appears that grabstackstr() will barf with such alignments
1081 * because stalloc() will return a string allocated in a new stackblock.
1082 */
1083#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1084enum {
1085 /* Most machines require the value returned from malloc to be aligned
1086 * in some way. The following macro will get this right
1087 * on many machines. */
1088 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1089 /* Minimum size of a block */
1090 MINSIZE = SHELL_ALIGN(504),
1091};
1092
1093struct stack_block {
1094 struct stack_block *prev;
1095 char space[MINSIZE];
1096};
1097
1098struct stackmark {
1099 struct stack_block *stackp;
1100 char *stacknxt;
1101 size_t stacknleft;
1102 struct stackmark *marknext;
1103};
1104
1105static struct stack_block stackbase;
1106static struct stack_block *stackp = &stackbase;
1107static struct stackmark *markp;
1108static char *stacknxt = stackbase.space;
1109static size_t stacknleft = MINSIZE;
1110static char *sstrend = stackbase.space + MINSIZE;
1111static int herefd = -1;
1112
1113#define stackblock() ((void *)stacknxt)
1114#define stackblocksize() stacknleft
1115
1116static void *
1117ckrealloc(void * p, size_t nbytes)
1118{
1119 p = realloc(p, nbytes);
1120 if (!p)
1121 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1122 return p;
1123}
1124
1125static void *
1126ckmalloc(size_t nbytes)
1127{
1128 return ckrealloc(NULL, nbytes);
1129}
1130
1131/*
1132 * Make a copy of a string in safe storage.
1133 */
1134static char *
1135ckstrdup(const char *s)
1136{
1137 char *p = strdup(s);
1138 if (!p)
1139 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1140 return p;
1141}
1142
1143/*
1144 * Parse trees for commands are allocated in lifo order, so we use a stack
1145 * to make this more efficient, and also to avoid all sorts of exception
1146 * handling code to handle interrupts in the middle of a parse.
1147 *
1148 * The size 504 was chosen because the Ultrix malloc handles that size
1149 * well.
1150 */
1151static void *
1152stalloc(size_t nbytes)
1153{
1154 char *p;
1155 size_t aligned;
1156
1157 aligned = SHELL_ALIGN(nbytes);
1158 if (aligned > stacknleft) {
1159 size_t len;
1160 size_t blocksize;
1161 struct stack_block *sp;
1162
1163 blocksize = aligned;
1164 if (blocksize < MINSIZE)
1165 blocksize = MINSIZE;
1166 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1167 if (len < blocksize)
1168 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1169 INT_OFF;
1170 sp = ckmalloc(len);
1171 sp->prev = stackp;
1172 stacknxt = sp->space;
1173 stacknleft = blocksize;
1174 sstrend = stacknxt + blocksize;
1175 stackp = sp;
1176 INT_ON;
1177 }
1178 p = stacknxt;
1179 stacknxt += aligned;
1180 stacknleft -= aligned;
1181 return p;
1182}
1183
1184static void
1185stunalloc(void *p)
1186{
1187#if DEBUG
1188 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
1189 write(2, "stunalloc\n", 10);
1190 abort();
1191 }
1192#endif
1193 stacknleft += stacknxt - (char *)p;
1194 stacknxt = p;
1195}
1196
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001197/*
1198 * Like strdup but works with the ash stack.
1199 */
1200static char *
1201ststrdup(const char *p)
1202{
1203 size_t len = strlen(p) + 1;
1204 return memcpy(stalloc(len), p, len);
1205}
1206
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001207static void
1208setstackmark(struct stackmark *mark)
1209{
1210 mark->stackp = stackp;
1211 mark->stacknxt = stacknxt;
1212 mark->stacknleft = stacknleft;
1213 mark->marknext = markp;
1214 markp = mark;
1215}
1216
1217static void
1218popstackmark(struct stackmark *mark)
1219{
1220 struct stack_block *sp;
1221
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001222 if (!mark->stackp)
1223 return;
1224
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001225 INT_OFF;
1226 markp = mark->marknext;
1227 while (stackp != mark->stackp) {
1228 sp = stackp;
1229 stackp = sp->prev;
1230 free(sp);
1231 }
1232 stacknxt = mark->stacknxt;
1233 stacknleft = mark->stacknleft;
1234 sstrend = mark->stacknxt + mark->stacknleft;
1235 INT_ON;
1236}
1237
1238/*
1239 * When the parser reads in a string, it wants to stick the string on the
1240 * stack and only adjust the stack pointer when it knows how big the
1241 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1242 * of space on top of the stack and stackblocklen returns the length of
1243 * this block. Growstackblock will grow this space by at least one byte,
1244 * possibly moving it (like realloc). Grabstackblock actually allocates the
1245 * part of the block that has been used.
1246 */
1247static void
1248growstackblock(void)
1249{
1250 size_t newlen;
1251
1252 newlen = stacknleft * 2;
1253 if (newlen < stacknleft)
1254 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1255 if (newlen < 128)
1256 newlen += 128;
1257
1258 if (stacknxt == stackp->space && stackp != &stackbase) {
1259 struct stack_block *oldstackp;
1260 struct stackmark *xmark;
1261 struct stack_block *sp;
1262 struct stack_block *prevstackp;
1263 size_t grosslen;
1264
1265 INT_OFF;
1266 oldstackp = stackp;
1267 sp = stackp;
1268 prevstackp = sp->prev;
1269 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1270 sp = ckrealloc(sp, grosslen);
1271 sp->prev = prevstackp;
1272 stackp = sp;
1273 stacknxt = sp->space;
1274 stacknleft = newlen;
1275 sstrend = sp->space + newlen;
1276
1277 /*
1278 * Stack marks pointing to the start of the old block
1279 * must be relocated to point to the new block
1280 */
1281 xmark = markp;
1282 while (xmark != NULL && xmark->stackp == oldstackp) {
1283 xmark->stackp = stackp;
1284 xmark->stacknxt = stacknxt;
1285 xmark->stacknleft = stacknleft;
1286 xmark = xmark->marknext;
1287 }
1288 INT_ON;
1289 } else {
1290 char *oldspace = stacknxt;
1291 int oldlen = stacknleft;
1292 char *p = stalloc(newlen);
1293
1294 /* free the space we just allocated */
1295 stacknxt = memcpy(p, oldspace, oldlen);
1296 stacknleft += newlen;
1297 }
1298}
1299
1300static void
1301grabstackblock(size_t len)
1302{
1303 len = SHELL_ALIGN(len);
1304 stacknxt += len;
1305 stacknleft -= len;
1306}
1307
1308/*
1309 * The following routines are somewhat easier to use than the above.
1310 * The user declares a variable of type STACKSTR, which may be declared
1311 * to be a register. The macro STARTSTACKSTR initializes things. Then
1312 * the user uses the macro STPUTC to add characters to the string. In
1313 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1314 * grown as necessary. When the user is done, she can just leave the
1315 * string there and refer to it using stackblock(). Or she can allocate
1316 * the space for it using grabstackstr(). If it is necessary to allow
1317 * someone else to use the stack temporarily and then continue to grow
1318 * the string, the user should use grabstack to allocate the space, and
1319 * then call ungrabstr(p) to return to the previous mode of operation.
1320 *
1321 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1322 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1323 * is space for at least one character.
1324 */
1325static void *
1326growstackstr(void)
1327{
1328 size_t len = stackblocksize();
1329 if (herefd >= 0 && len >= 1024) {
1330 full_write(herefd, stackblock(), len);
1331 return stackblock();
1332 }
1333 growstackblock();
1334 return stackblock() + len;
1335}
1336
1337/*
1338 * Called from CHECKSTRSPACE.
1339 */
1340static char *
1341makestrspace(size_t newlen, char *p)
1342{
1343 size_t len = p - stacknxt;
1344 size_t size = stackblocksize();
1345
1346 for (;;) {
1347 size_t nleft;
1348
1349 size = stackblocksize();
1350 nleft = size - len;
1351 if (nleft >= newlen)
1352 break;
1353 growstackblock();
1354 }
1355 return stackblock() + len;
1356}
1357
1358static char *
1359stack_nputstr(const char *s, size_t n, char *p)
1360{
1361 p = makestrspace(n, p);
1362 p = memcpy(p, s, n) + n;
1363 return p;
1364}
1365
1366static char *
1367stack_putstr(const char *s, char *p)
1368{
1369 return stack_nputstr(s, strlen(s), p);
1370}
1371
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001372static char *
1373_STPUTC(int c, char *p)
1374{
1375 if (p == sstrend)
1376 p = growstackstr();
1377 *p++ = c;
1378 return p;
1379}
1380
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001381#define STARTSTACKSTR(p) ((p) = stackblock())
1382#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001383#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001384 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001385 char *q = (p); \
1386 size_t l = (n); \
1387 size_t m = sstrend - q; \
1388 if (l > m) \
1389 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001390 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001391#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001392#define STACKSTRNUL(p) \
1393 do { \
1394 if ((p) == sstrend) \
1395 p = growstackstr(); \
1396 *p = '\0'; \
1397 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001398#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001399#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001400#define STADJUST(amount, p) (p += (amount))
1401
1402#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1403#define ungrabstackstr(s, p) stunalloc((s))
1404#define stackstrend() ((void *)sstrend)
1405
1406
1407/* ============ String helpers */
1408
1409/*
1410 * prefix -- see if pfx is a prefix of string.
1411 */
1412static char *
1413prefix(const char *string, const char *pfx)
1414{
1415 while (*pfx) {
1416 if (*pfx++ != *string++)
1417 return 0;
1418 }
1419 return (char *) string;
1420}
1421
1422/*
1423 * Check for a valid number. This should be elsewhere.
1424 */
1425static int
1426is_number(const char *p)
1427{
1428 do {
1429 if (!isdigit(*p))
1430 return 0;
1431 } while (*++p != '\0');
1432 return 1;
1433}
1434
1435/*
1436 * Convert a string of digits to an integer, printing an error message on
1437 * failure.
1438 */
1439static int
1440number(const char *s)
1441{
1442 if (!is_number(s))
1443 ash_msg_and_raise_error(illnum, s);
1444 return atoi(s);
1445}
1446
1447/*
1448 * Produce a possibly single quoted string suitable as input to the shell.
1449 * The return string is allocated on the stack.
1450 */
1451static char *
1452single_quote(const char *s)
1453{
1454 char *p;
1455
1456 STARTSTACKSTR(p);
1457
1458 do {
1459 char *q;
1460 size_t len;
1461
1462 len = strchrnul(s, '\'') - s;
1463
1464 q = p = makestrspace(len + 3, p);
1465
1466 *q++ = '\'';
1467 q = memcpy(q, s, len) + len;
1468 *q++ = '\'';
1469 s += len;
1470
1471 STADJUST(q - p, p);
1472
1473 len = strspn(s, "'");
1474 if (!len)
1475 break;
1476
1477 q = p = makestrspace(len + 3, p);
1478
1479 *q++ = '"';
1480 q = memcpy(q, s, len) + len;
1481 *q++ = '"';
1482 s += len;
1483
1484 STADJUST(q - p, p);
1485 } while (*s);
1486
1487 USTPUTC(0, p);
1488
1489 return stackblock();
1490}
1491
1492
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001493/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001494
1495static char **argptr; /* argument list for builtin commands */
1496static char *optionarg; /* set by nextopt (like getopt) */
1497static char *optptr; /* used by nextopt */
1498
1499/*
1500 * XXX - should get rid of. have all builtins use getopt(3). the
1501 * library getopt must have the BSD extension static variable "optreset"
1502 * otherwise it can't be used within the shell safely.
1503 *
1504 * Standard option processing (a la getopt) for builtin routines. The
1505 * only argument that is passed to nextopt is the option string; the
1506 * other arguments are unnecessary. It return the character, or '\0' on
1507 * end of input.
1508 */
1509static int
1510nextopt(const char *optstring)
1511{
1512 char *p;
1513 const char *q;
1514 char c;
1515
1516 p = optptr;
1517 if (p == NULL || *p == '\0') {
1518 p = *argptr;
1519 if (p == NULL || *p != '-' || *++p == '\0')
1520 return '\0';
1521 argptr++;
1522 if (LONE_DASH(p)) /* check for "--" */
1523 return '\0';
1524 }
1525 c = *p++;
1526 for (q = optstring; *q != c; ) {
1527 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001528 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001529 if (*++q == ':')
1530 q++;
1531 }
1532 if (*++q == ':') {
1533 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001534 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001535 optionarg = p;
1536 p = NULL;
1537 }
1538 optptr = p;
1539 return c;
1540}
1541
1542
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001543/* ============ Math support definitions */
1544
1545#if ENABLE_ASH_MATH_SUPPORT_64
1546typedef int64_t arith_t;
1547#define arith_t_type long long
1548#else
1549typedef long arith_t;
1550#define arith_t_type long
1551#endif
1552
1553#if ENABLE_ASH_MATH_SUPPORT
1554static arith_t dash_arith(const char *);
1555static arith_t arith(const char *expr, int *perrcode);
1556#endif
1557
1558#if ENABLE_ASH_RANDOM_SUPPORT
1559static unsigned long rseed;
1560#ifndef DYNAMIC_VAR
1561#define DYNAMIC_VAR
1562#endif
1563#endif
1564
1565
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001566/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001567
1568/* flags */
1569#define VEXPORT 0x01 /* variable is exported */
1570#define VREADONLY 0x02 /* variable cannot be modified */
1571#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1572#define VTEXTFIXED 0x08 /* text is statically allocated */
1573#define VSTACK 0x10 /* text is allocated on the stack */
1574#define VUNSET 0x20 /* the variable is not set */
1575#define VNOFUNC 0x40 /* don't call the callback function */
1576#define VNOSET 0x80 /* do not set variable - just readonly test */
1577#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1578#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001579# define VDYNAMIC 0x200 /* dynamic variable */
1580#else
1581# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001582#endif
1583
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001584static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1585#ifdef IFS_BROKEN
1586static const char defifsvar[] = "IFS= \t\n";
1587#define defifs (defifsvar + 4)
1588#else
1589static const char defifs[] = " \t\n";
1590#endif
1591
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001592struct shparam {
1593 int nparam; /* # of positional parameters (without $0) */
1594 unsigned char malloc; /* if parameter list dynamically allocated */
1595 char **p; /* parameter list */
1596#if ENABLE_ASH_GETOPTS
1597 int optind; /* next parameter to be processed by getopts */
1598 int optoff; /* used by getopts */
1599#endif
1600};
1601
1602static struct shparam shellparam; /* $@ current positional parameters */
1603
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00001604/*
1605 * Free the list of positional parameters.
1606 */
1607static void
1608freeparam(volatile struct shparam *param)
1609{
1610 char **ap;
1611
1612 if (param->malloc) {
1613 for (ap = param->p; *ap; ap++)
1614 free(*ap);
1615 free(param->p);
1616 }
1617}
1618
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001619#if ENABLE_ASH_GETOPTS
1620static void
1621getoptsreset(const char *value)
1622{
1623 shellparam.optind = number(value);
1624 shellparam.optoff = -1;
1625}
1626#endif
1627
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001628struct var {
1629 struct var *next; /* next entry in hash list */
1630 int flags; /* flags are defined above */
1631 const char *text; /* name=value */
1632 void (*func)(const char *); /* function to be called when */
1633 /* the variable gets set/unset */
1634};
1635
1636struct localvar {
1637 struct localvar *next; /* next local variable in list */
1638 struct var *vp; /* the variable that was made local */
1639 int flags; /* saved flags */
1640 const char *text; /* saved text */
1641};
1642
1643/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001644#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001645static void
1646change_lc_all(const char *value)
1647{
1648 if (value && *value != '\0')
1649 setlocale(LC_ALL, value);
1650}
1651static void
1652change_lc_ctype(const char *value)
1653{
1654 if (value && *value != '\0')
1655 setlocale(LC_CTYPE, value);
1656}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001657#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001658#if ENABLE_ASH_MAIL
1659static void chkmail(void);
1660static void changemail(const char *);
1661#endif
1662static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001663#if ENABLE_ASH_RANDOM_SUPPORT
1664static void change_random(const char *);
1665#endif
1666
1667static struct var varinit[] = {
1668#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001669 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001670#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001671 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001672#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001673#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001674 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1675 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001676#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001677 { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1678 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1679 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1680 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001681#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001682 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001683#endif
1684#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001685 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001686#endif
1687#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001688 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1689 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001690#endif
1691#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001692 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001693#endif
1694};
1695
1696#define vifs varinit[0]
1697#if ENABLE_ASH_MAIL
1698#define vmail (&vifs)[1]
1699#define vmpath (&vmail)[1]
1700#else
1701#define vmpath vifs
1702#endif
1703#define vpath (&vmpath)[1]
1704#define vps1 (&vpath)[1]
1705#define vps2 (&vps1)[1]
1706#define vps4 (&vps2)[1]
1707#define voptind (&vps4)[1]
1708#if ENABLE_ASH_GETOPTS
1709#define vrandom (&voptind)[1]
1710#else
1711#define vrandom (&vps4)[1]
1712#endif
1713#define defpath (defpathvar + 5)
1714
1715/*
1716 * The following macros access the values of the above variables.
1717 * They have to skip over the name. They return the null string
1718 * for unset variables.
1719 */
1720#define ifsval() (vifs.text + 4)
1721#define ifsset() ((vifs.flags & VUNSET) == 0)
1722#define mailval() (vmail.text + 5)
1723#define mpathval() (vmpath.text + 9)
1724#define pathval() (vpath.text + 5)
1725#define ps1val() (vps1.text + 4)
1726#define ps2val() (vps2.text + 4)
1727#define ps4val() (vps4.text + 4)
1728#define optindval() (voptind.text + 7)
1729
1730#define mpathset() ((vmpath.flags & VUNSET) == 0)
1731
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732/*
1733 * The parsefile structure pointed to by the global variable parsefile
1734 * contains information about the current file being read.
1735 */
1736struct redirtab {
1737 struct redirtab *next;
1738 int renamed[10];
1739 int nullredirs;
1740};
1741
1742static struct redirtab *redirlist;
1743static int nullredirs;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1745
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746#define VTABSIZE 39
1747
1748static struct var *vartab[VTABSIZE];
1749
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001750#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1751#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1752
1753/*
1754 * Return of a legal variable name (a letter or underscore followed by zero or
1755 * more letters, underscores, and digits).
1756 */
1757static char *
1758endofname(const char *name)
1759{
1760 char *p;
1761
1762 p = (char *) name;
1763 if (!is_name(*p))
1764 return p;
1765 while (*++p) {
1766 if (!is_in_name(*p))
1767 break;
1768 }
1769 return p;
1770}
1771
1772/*
1773 * Compares two strings up to the first = or '\0'. The first
1774 * string must be terminated by '='; the second may be terminated by
1775 * either '=' or '\0'.
1776 */
1777static int
1778varcmp(const char *p, const char *q)
1779{
1780 int c, d;
1781
1782 while ((c = *p) == (d = *q)) {
1783 if (!c || c == '=')
1784 goto out;
1785 p++;
1786 q++;
1787 }
1788 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001789 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001791 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001792 out:
1793 return c - d;
1794}
1795
1796static int
1797varequal(const char *a, const char *b)
1798{
1799 return !varcmp(a, b);
1800}
1801
1802/*
1803 * Find the appropriate entry in the hash table from the name.
1804 */
1805static struct var **
1806hashvar(const char *p)
1807{
1808 unsigned hashval;
1809
1810 hashval = ((unsigned char) *p) << 4;
1811 while (*p && *p != '=')
1812 hashval += (unsigned char) *p++;
1813 return &vartab[hashval % VTABSIZE];
1814}
1815
1816static int
1817vpcmp(const void *a, const void *b)
1818{
1819 return varcmp(*(const char **)a, *(const char **)b);
1820}
1821
1822/*
1823 * This routine initializes the builtin variables.
1824 */
1825static void
1826initvar(void)
1827{
1828 struct var *vp;
1829 struct var *end;
1830 struct var **vpp;
1831
1832 /*
1833 * PS1 depends on uid
1834 */
1835#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1836 vps1.text = "PS1=\\w \\$ ";
1837#else
1838 if (!geteuid())
1839 vps1.text = "PS1=# ";
1840#endif
1841 vp = varinit;
1842 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1843 do {
1844 vpp = hashvar(vp->text);
1845 vp->next = *vpp;
1846 *vpp = vp;
1847 } while (++vp < end);
1848}
1849
1850static struct var **
1851findvar(struct var **vpp, const char *name)
1852{
1853 for (; *vpp; vpp = &(*vpp)->next) {
1854 if (varequal((*vpp)->text, name)) {
1855 break;
1856 }
1857 }
1858 return vpp;
1859}
1860
1861/*
1862 * Find the value of a variable. Returns NULL if not set.
1863 */
1864static char *
1865lookupvar(const char *name)
1866{
1867 struct var *v;
1868
1869 v = *findvar(hashvar(name), name);
1870 if (v) {
1871#ifdef DYNAMIC_VAR
1872 /*
1873 * Dynamic variables are implemented roughly the same way they are
1874 * in bash. Namely, they're "special" so long as they aren't unset.
1875 * As soon as they're unset, they're no longer dynamic, and dynamic
1876 * lookup will no longer happen at that point. -- PFM.
1877 */
1878 if ((v->flags & VDYNAMIC))
1879 (*v->func)(NULL);
1880#endif
1881 if (!(v->flags & VUNSET))
1882 return strchrnul(v->text, '=') + 1;
1883 }
1884 return NULL;
1885}
1886
1887/*
1888 * Search the environment of a builtin command.
1889 */
1890static char *
1891bltinlookup(const char *name)
1892{
1893 struct strlist *sp;
1894
1895 for (sp = cmdenviron; sp; sp = sp->next) {
1896 if (varequal(sp->text, name))
1897 return strchrnul(sp->text, '=') + 1;
1898 }
1899 return lookupvar(name);
1900}
1901
1902/*
1903 * Same as setvar except that the variable and value are passed in
1904 * the first argument as name=value. Since the first argument will
1905 * be actually stored in the table, it should not be a string that
1906 * will go away.
1907 * Called with interrupts off.
1908 */
1909static void
1910setvareq(char *s, int flags)
1911{
1912 struct var *vp, **vpp;
1913
1914 vpp = hashvar(s);
1915 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1916 vp = *findvar(vpp, s);
1917 if (vp) {
1918 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1919 const char *n;
1920
1921 if (flags & VNOSAVE)
1922 free(s);
1923 n = vp->text;
1924 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1925 }
1926
1927 if (flags & VNOSET)
1928 return;
1929
1930 if (vp->func && (flags & VNOFUNC) == 0)
1931 (*vp->func)(strchrnul(s, '=') + 1);
1932
1933 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1934 free((char*)vp->text);
1935
1936 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1937 } else {
1938 if (flags & VNOSET)
1939 return;
1940 /* not found */
1941 vp = ckmalloc(sizeof(*vp));
1942 vp->next = *vpp;
1943 vp->func = NULL;
1944 *vpp = vp;
1945 }
1946 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1947 s = ckstrdup(s);
1948 vp->text = s;
1949 vp->flags = flags;
1950}
1951
1952/*
1953 * Set the value of a variable. The flags argument is ored with the
1954 * flags of the variable. If val is NULL, the variable is unset.
1955 */
1956static void
1957setvar(const char *name, const char *val, int flags)
1958{
1959 char *p, *q;
1960 size_t namelen;
1961 char *nameeq;
1962 size_t vallen;
1963
1964 q = endofname(name);
1965 p = strchrnul(q, '=');
1966 namelen = p - name;
1967 if (!namelen || p != q)
1968 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1969 vallen = 0;
1970 if (val == NULL) {
1971 flags |= VUNSET;
1972 } else {
1973 vallen = strlen(val);
1974 }
1975 INT_OFF;
1976 nameeq = ckmalloc(namelen + vallen + 2);
1977 p = memcpy(nameeq, name, namelen) + namelen;
1978 if (val) {
1979 *p++ = '=';
1980 p = memcpy(p, val, vallen) + vallen;
1981 }
1982 *p = '\0';
1983 setvareq(nameeq, flags | VNOSAVE);
1984 INT_ON;
1985}
1986
1987#if ENABLE_ASH_GETOPTS
1988/*
1989 * Safe version of setvar, returns 1 on success 0 on failure.
1990 */
1991static int
1992setvarsafe(const char *name, const char *val, int flags)
1993{
1994 int err;
1995 volatile int saveint;
1996 struct jmploc *volatile savehandler = exception_handler;
1997 struct jmploc jmploc;
1998
1999 SAVE_INT(saveint);
2000 if (setjmp(jmploc.loc))
2001 err = 1;
2002 else {
2003 exception_handler = &jmploc;
2004 setvar(name, val, flags);
2005 err = 0;
2006 }
2007 exception_handler = savehandler;
2008 RESTORE_INT(saveint);
2009 return err;
2010}
2011#endif
2012
2013/*
2014 * Unset the specified variable.
2015 */
2016static int
2017unsetvar(const char *s)
2018{
2019 struct var **vpp;
2020 struct var *vp;
2021 int retval;
2022
2023 vpp = findvar(hashvar(s), s);
2024 vp = *vpp;
2025 retval = 2;
2026 if (vp) {
2027 int flags = vp->flags;
2028
2029 retval = 1;
2030 if (flags & VREADONLY)
2031 goto out;
2032#ifdef DYNAMIC_VAR
2033 vp->flags &= ~VDYNAMIC;
2034#endif
2035 if (flags & VUNSET)
2036 goto ok;
2037 if ((flags & VSTRFIXED) == 0) {
2038 INT_OFF;
2039 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2040 free((char*)vp->text);
2041 *vpp = vp->next;
2042 free(vp);
2043 INT_ON;
2044 } else {
2045 setvar(s, 0, 0);
2046 vp->flags &= ~VEXPORT;
2047 }
2048 ok:
2049 retval = 0;
2050 }
2051 out:
2052 return retval;
2053}
2054
2055/*
2056 * Process a linked list of variable assignments.
2057 */
2058static void
2059listsetvar(struct strlist *list_set_var, int flags)
2060{
2061 struct strlist *lp = list_set_var;
2062
2063 if (!lp)
2064 return;
2065 INT_OFF;
2066 do {
2067 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002068 lp = lp->next;
2069 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002070 INT_ON;
2071}
2072
2073/*
2074 * Generate a list of variables satisfying the given conditions.
2075 */
2076static char **
2077listvars(int on, int off, char ***end)
2078{
2079 struct var **vpp;
2080 struct var *vp;
2081 char **ep;
2082 int mask;
2083
2084 STARTSTACKSTR(ep);
2085 vpp = vartab;
2086 mask = on | off;
2087 do {
2088 for (vp = *vpp; vp; vp = vp->next) {
2089 if ((vp->flags & mask) == on) {
2090 if (ep == stackstrend())
2091 ep = growstackstr();
2092 *ep++ = (char *) vp->text;
2093 }
2094 }
2095 } while (++vpp < vartab + VTABSIZE);
2096 if (ep == stackstrend())
2097 ep = growstackstr();
2098 if (end)
2099 *end = ep;
2100 *ep++ = NULL;
2101 return grabstackstr(ep);
2102}
2103
2104
2105/* ============ Path search helper
2106 *
2107 * The variable path (passed by reference) should be set to the start
2108 * of the path before the first call; padvance will update
2109 * this value as it proceeds. Successive calls to padvance will return
2110 * the possible path expansions in sequence. If an option (indicated by
2111 * a percent sign) appears in the path entry then the global variable
2112 * pathopt will be set to point to it; otherwise pathopt will be set to
2113 * NULL.
2114 */
2115static const char *pathopt; /* set by padvance */
2116
2117static char *
2118padvance(const char **path, const char *name)
2119{
2120 const char *p;
2121 char *q;
2122 const char *start;
2123 size_t len;
2124
2125 if (*path == NULL)
2126 return NULL;
2127 start = *path;
2128 for (p = start; *p && *p != ':' && *p != '%'; p++);
2129 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2130 while (stackblocksize() < len)
2131 growstackblock();
2132 q = stackblock();
2133 if (p != start) {
2134 memcpy(q, start, p - start);
2135 q += p - start;
2136 *q++ = '/';
2137 }
2138 strcpy(q, name);
2139 pathopt = NULL;
2140 if (*p == '%') {
2141 pathopt = ++p;
2142 while (*p && *p != ':') p++;
2143 }
2144 if (*p == ':')
2145 *path = p + 1;
2146 else
2147 *path = NULL;
2148 return stalloc(len);
2149}
2150
2151
2152/* ============ Prompt */
2153
2154static int doprompt; /* if set, prompt the user */
2155static int needprompt; /* true if interactive and at start of line */
2156
2157#if ENABLE_FEATURE_EDITING
2158static line_input_t *line_input_state;
2159static const char *cmdedit_prompt;
2160static void
2161putprompt(const char *s)
2162{
2163 if (ENABLE_ASH_EXPAND_PRMT) {
2164 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002165 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002166 return;
2167 }
2168 cmdedit_prompt = s;
2169}
2170#else
2171static void
2172putprompt(const char *s)
2173{
2174 out2str(s);
2175}
2176#endif
2177
2178#if ENABLE_ASH_EXPAND_PRMT
2179/* expandstr() needs parsing machinery, so it is far away ahead... */
2180static const char *expandstr(const char *ps);
2181#else
2182#define expandstr(s) s
2183#endif
2184
2185static void
2186setprompt(int whichprompt)
2187{
2188 const char *prompt;
2189#if ENABLE_ASH_EXPAND_PRMT
2190 struct stackmark smark;
2191#endif
2192
2193 needprompt = 0;
2194
2195 switch (whichprompt) {
2196 case 1:
2197 prompt = ps1val();
2198 break;
2199 case 2:
2200 prompt = ps2val();
2201 break;
2202 default: /* 0 */
2203 prompt = nullstr;
2204 }
2205#if ENABLE_ASH_EXPAND_PRMT
2206 setstackmark(&smark);
2207 stalloc(stackblocksize());
2208#endif
2209 putprompt(expandstr(prompt));
2210#if ENABLE_ASH_EXPAND_PRMT
2211 popstackmark(&smark);
2212#endif
2213}
2214
2215
2216/* ============ The cd and pwd commands */
2217
2218#define CD_PHYSICAL 1
2219#define CD_PRINT 2
2220
2221static int docd(const char *, int);
2222
2223static char *curdir = nullstr; /* current working directory */
2224static char *physdir = nullstr; /* physical working directory */
2225
2226static int
2227cdopt(void)
2228{
2229 int flags = 0;
2230 int i, j;
2231
2232 j = 'L';
2233 while ((i = nextopt("LP"))) {
2234 if (i != j) {
2235 flags ^= CD_PHYSICAL;
2236 j = i;
2237 }
2238 }
2239
2240 return flags;
2241}
2242
2243/*
2244 * Update curdir (the name of the current directory) in response to a
2245 * cd command.
2246 */
2247static const char *
2248updatepwd(const char *dir)
2249{
2250 char *new;
2251 char *p;
2252 char *cdcomppath;
2253 const char *lim;
2254
2255 cdcomppath = ststrdup(dir);
2256 STARTSTACKSTR(new);
2257 if (*dir != '/') {
2258 if (curdir == nullstr)
2259 return 0;
2260 new = stack_putstr(curdir, new);
2261 }
2262 new = makestrspace(strlen(dir) + 2, new);
2263 lim = stackblock() + 1;
2264 if (*dir != '/') {
2265 if (new[-1] != '/')
2266 USTPUTC('/', new);
2267 if (new > lim && *lim == '/')
2268 lim++;
2269 } else {
2270 USTPUTC('/', new);
2271 cdcomppath++;
2272 if (dir[1] == '/' && dir[2] != '/') {
2273 USTPUTC('/', new);
2274 cdcomppath++;
2275 lim++;
2276 }
2277 }
2278 p = strtok(cdcomppath, "/");
2279 while (p) {
2280 switch (*p) {
2281 case '.':
2282 if (p[1] == '.' && p[2] == '\0') {
2283 while (new > lim) {
2284 STUNPUTC(new);
2285 if (new[-1] == '/')
2286 break;
2287 }
2288 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002289 }
2290 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002291 break;
2292 /* fall through */
2293 default:
2294 new = stack_putstr(p, new);
2295 USTPUTC('/', new);
2296 }
2297 p = strtok(0, "/");
2298 }
2299 if (new > lim)
2300 STUNPUTC(new);
2301 *new = 0;
2302 return stackblock();
2303}
2304
2305/*
2306 * Find out what the current directory is. If we already know the current
2307 * directory, this routine returns immediately.
2308 */
2309static char *
2310getpwd(void)
2311{
2312 char *dir = getcwd(0, 0);
2313 return dir ? dir : nullstr;
2314}
2315
2316static void
2317setpwd(const char *val, int setold)
2318{
2319 char *oldcur, *dir;
2320
2321 oldcur = dir = curdir;
2322
2323 if (setold) {
2324 setvar("OLDPWD", oldcur, VEXPORT);
2325 }
2326 INT_OFF;
2327 if (physdir != nullstr) {
2328 if (physdir != oldcur)
2329 free(physdir);
2330 physdir = nullstr;
2331 }
2332 if (oldcur == val || !val) {
2333 char *s = getpwd();
2334 physdir = s;
2335 if (!val)
2336 dir = s;
2337 } else
2338 dir = ckstrdup(val);
2339 if (oldcur != dir && oldcur != nullstr) {
2340 free(oldcur);
2341 }
2342 curdir = dir;
2343 INT_ON;
2344 setvar("PWD", dir, VEXPORT);
2345}
2346
2347static void hashcd(void);
2348
2349/*
2350 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2351 * know that the current directory has changed.
2352 */
2353static int
2354docd(const char *dest, int flags)
2355{
2356 const char *dir = 0;
2357 int err;
2358
2359 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2360
2361 INT_OFF;
2362 if (!(flags & CD_PHYSICAL)) {
2363 dir = updatepwd(dest);
2364 if (dir)
2365 dest = dir;
2366 }
2367 err = chdir(dest);
2368 if (err)
2369 goto out;
2370 setpwd(dir, 1);
2371 hashcd();
2372 out:
2373 INT_ON;
2374 return err;
2375}
2376
2377static int
2378cdcmd(int argc, char **argv)
2379{
2380 const char *dest;
2381 const char *path;
2382 const char *p;
2383 char c;
2384 struct stat statb;
2385 int flags;
2386
2387 flags = cdopt();
2388 dest = *argptr;
2389 if (!dest)
2390 dest = bltinlookup(homestr);
2391 else if (LONE_DASH(dest)) {
2392 dest = bltinlookup("OLDPWD");
2393 flags |= CD_PRINT;
2394 }
2395 if (!dest)
2396 dest = nullstr;
2397 if (*dest == '/')
2398 goto step7;
2399 if (*dest == '.') {
2400 c = dest[1];
2401 dotdot:
2402 switch (c) {
2403 case '\0':
2404 case '/':
2405 goto step6;
2406 case '.':
2407 c = dest[2];
2408 if (c != '.')
2409 goto dotdot;
2410 }
2411 }
2412 if (!*dest)
2413 dest = ".";
2414 path = bltinlookup("CDPATH");
2415 if (!path) {
2416 step6:
2417 step7:
2418 p = dest;
2419 goto docd;
2420 }
2421 do {
2422 c = *path;
2423 p = padvance(&path, dest);
2424 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2425 if (c && c != ':')
2426 flags |= CD_PRINT;
2427 docd:
2428 if (!docd(p, flags))
2429 goto out;
2430 break;
2431 }
2432 } while (path);
2433 ash_msg_and_raise_error("can't cd to %s", dest);
2434 /* NOTREACHED */
2435 out:
2436 if (flags & CD_PRINT)
2437 out1fmt(snlfmt, curdir);
2438 return 0;
2439}
2440
2441static int
2442pwdcmd(int argc, char **argv)
2443{
2444 int flags;
2445 const char *dir = curdir;
2446
2447 flags = cdopt();
2448 if (flags) {
2449 if (physdir == nullstr)
2450 setpwd(dir, 0);
2451 dir = physdir;
2452 }
2453 out1fmt(snlfmt, dir);
2454 return 0;
2455}
2456
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002457
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002458/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002459
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002460#define IBUFSIZ (BUFSIZ + 1)
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002461#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002462
Eric Andersenc470f442003-07-28 09:56:35 +00002463/* Syntax classes */
2464#define CWORD 0 /* character is nothing special */
2465#define CNL 1 /* newline character */
2466#define CBACK 2 /* a backslash character */
2467#define CSQUOTE 3 /* single quote */
2468#define CDQUOTE 4 /* double quote */
2469#define CENDQUOTE 5 /* a terminating quote */
2470#define CBQUOTE 6 /* backwards single quote */
2471#define CVAR 7 /* a dollar sign */
2472#define CENDVAR 8 /* a '}' character */
2473#define CLP 9 /* a left paren in arithmetic */
2474#define CRP 10 /* a right paren in arithmetic */
2475#define CENDFILE 11 /* end of file */
2476#define CCTL 12 /* like CWORD, except it must be escaped */
2477#define CSPCL 13 /* these terminate a word */
2478#define CIGN 14 /* character should be ignored */
2479
Denis Vlasenko131ae172007-02-18 13:00:19 +00002480#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002481#define SYNBASE 130
2482#define PEOF -130
2483#define PEOA -129
2484#define PEOA_OR_PEOF PEOA
2485#else
2486#define SYNBASE 129
2487#define PEOF -129
2488#define PEOA_OR_PEOF PEOF
2489#endif
2490
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002491/* number syntax index */
2492#define BASESYNTAX 0 /* not in quotes */
2493#define DQSYNTAX 1 /* in double quotes */
2494#define SQSYNTAX 2 /* in single quotes */
2495#define ARISYNTAX 3 /* in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002496
Denis Vlasenko131ae172007-02-18 13:00:19 +00002497#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002498#define USE_SIT_FUNCTION
2499#endif
2500
Denis Vlasenko131ae172007-02-18 13:00:19 +00002501#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002502static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002503#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002504 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002505#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002506 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2507 { CNL, CNL, CNL, CNL }, /* 2, \n */
2508 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2509 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2510 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2511 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2512 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2513 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2514 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2515 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2516 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002517#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002518 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2519 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2520 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002521#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002522};
Eric Andersenc470f442003-07-28 09:56:35 +00002523#else
2524static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002525#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002526 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002527#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002528 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2529 { CNL, CNL, CNL }, /* 2, \n */
2530 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2531 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2532 { CVAR, CVAR, CWORD }, /* 5, $ */
2533 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2534 { CSPCL, CWORD, CWORD }, /* 7, ( */
2535 { CSPCL, CWORD, CWORD }, /* 8, ) */
2536 { CBACK, CBACK, CCTL }, /* 9, \ */
2537 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2538 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002539#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002540 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2541 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2542 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002543#endif
2544};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002545#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002546
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002547#ifdef USE_SIT_FUNCTION
2548
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002549static int
2550SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002551{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002552 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002553#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002554 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002555 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2556 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2557 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2558 11, 3 /* "}~" */
2559 };
2560#else
2561 static const char syntax_index_table[] = {
2562 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2563 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2564 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2565 10, 2 /* "}~" */
2566 };
2567#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002568 const char *s;
2569 int indx;
2570
Eric Andersenc470f442003-07-28 09:56:35 +00002571 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002572 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002573#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002574 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002575 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002576 else
2577#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002578#define U_C(c) ((unsigned char)(c))
2579
2580 if ((unsigned char)c >= (unsigned char)(CTLESC)
2581 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2582 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002583 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002584 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002585 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002586 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002587 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002588 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002589 }
2590 return S_I_T[indx][syntax];
2591}
2592
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002593#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002594
Denis Vlasenko131ae172007-02-18 13:00:19 +00002595#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002596#define CSPCL_CIGN_CIGN_CIGN 0
2597#define CSPCL_CWORD_CWORD_CWORD 1
2598#define CNL_CNL_CNL_CNL 2
2599#define CWORD_CCTL_CCTL_CWORD 3
2600#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2601#define CVAR_CVAR_CWORD_CVAR 5
2602#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2603#define CSPCL_CWORD_CWORD_CLP 7
2604#define CSPCL_CWORD_CWORD_CRP 8
2605#define CBACK_CBACK_CCTL_CBACK 9
2606#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2607#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2608#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2609#define CWORD_CWORD_CWORD_CWORD 13
2610#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002611#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002612#define CSPCL_CWORD_CWORD_CWORD 0
2613#define CNL_CNL_CNL_CNL 1
2614#define CWORD_CCTL_CCTL_CWORD 2
2615#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2616#define CVAR_CVAR_CWORD_CVAR 4
2617#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2618#define CSPCL_CWORD_CWORD_CLP 6
2619#define CSPCL_CWORD_CWORD_CRP 7
2620#define CBACK_CBACK_CCTL_CBACK 8
2621#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2622#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2623#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2624#define CWORD_CWORD_CWORD_CWORD 12
2625#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002626#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002627
2628static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002629 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002630 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002631#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002632 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2633#endif
2634 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2635 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2636 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2637 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2638 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2639 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2640 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2641 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2642 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002643 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2644 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2645 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2646 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2647 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2648 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2649 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2650 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2772 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2773 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2795 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002796 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002797 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2798 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2799 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2800 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002801 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002802 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2803 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2804 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2805 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2806 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2807 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2808 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2809 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2810 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2811 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2812 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2813 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2814 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2815 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2816 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2817 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2818 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2819 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2820 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2821 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2822 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2823 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2824 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2825 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2826 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2827 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2828 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2829 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2830 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2831 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2832 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2833 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2851 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2852 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2853 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2854 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2855 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2856 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2857 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2858 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2859 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2860 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2861 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2862 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2863 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2865 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2866 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2887 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2888 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2889 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002890};
2891
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002892#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2893
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002894#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002895
Eric Andersen2870d962001-07-02 17:27:21 +00002896
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002897/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002898
Denis Vlasenko131ae172007-02-18 13:00:19 +00002899#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002900
2901#define ALIASINUSE 1
2902#define ALIASDEAD 2
2903
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002904#define ATABSIZE 39
2905
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002906struct alias {
2907 struct alias *next;
2908 char *name;
2909 char *val;
2910 int flag;
2911};
2912
Eric Andersen2870d962001-07-02 17:27:21 +00002913static struct alias *atab[ATABSIZE];
2914
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002915static struct alias **
2916__lookupalias(const char *name) {
2917 unsigned int hashval;
2918 struct alias **app;
2919 const char *p;
2920 unsigned int ch;
2921
2922 p = name;
2923
2924 ch = (unsigned char)*p;
2925 hashval = ch << 4;
2926 while (ch) {
2927 hashval += ch;
2928 ch = (unsigned char)*++p;
2929 }
2930 app = &atab[hashval % ATABSIZE];
2931
2932 for (; *app; app = &(*app)->next) {
2933 if (strcmp(name, (*app)->name) == 0) {
2934 break;
2935 }
2936 }
2937
2938 return app;
2939}
2940
2941static struct alias *
2942lookupalias(const char *name, int check)
2943{
2944 struct alias *ap = *__lookupalias(name);
2945
2946 if (check && ap && (ap->flag & ALIASINUSE))
2947 return NULL;
2948 return ap;
2949}
2950
2951static struct alias *
2952freealias(struct alias *ap)
2953{
2954 struct alias *next;
2955
2956 if (ap->flag & ALIASINUSE) {
2957 ap->flag |= ALIASDEAD;
2958 return ap;
2959 }
2960
2961 next = ap->next;
2962 free(ap->name);
2963 free(ap->val);
2964 free(ap);
2965 return next;
2966}
Eric Andersencb57d552001-06-28 07:25:16 +00002967
Eric Andersenc470f442003-07-28 09:56:35 +00002968static void
2969setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002970{
2971 struct alias *ap, **app;
2972
2973 app = __lookupalias(name);
2974 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002975 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002976 if (ap) {
2977 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002978 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002979 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002980 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002981 ap->flag &= ~ALIASDEAD;
2982 } else {
2983 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002984 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002985 ap->name = ckstrdup(name);
2986 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002987 ap->flag = 0;
2988 ap->next = 0;
2989 *app = ap;
2990 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002991 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002992}
2993
Eric Andersenc470f442003-07-28 09:56:35 +00002994static int
2995unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00002996{
Eric Andersencb57d552001-06-28 07:25:16 +00002997 struct alias **app;
2998
2999 app = __lookupalias(name);
3000
3001 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003002 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003003 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003004 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003005 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003006 }
3007
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003008 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003009}
3010
Eric Andersenc470f442003-07-28 09:56:35 +00003011static void
3012rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003013{
Eric Andersencb57d552001-06-28 07:25:16 +00003014 struct alias *ap, **app;
3015 int i;
3016
Denis Vlasenkob012b102007-02-19 22:43:01 +00003017 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003018 for (i = 0; i < ATABSIZE; i++) {
3019 app = &atab[i];
3020 for (ap = *app; ap; ap = *app) {
3021 *app = freealias(*app);
3022 if (ap == *app) {
3023 app = &ap->next;
3024 }
3025 }
3026 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003027 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003028}
3029
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003030static void
3031printalias(const struct alias *ap)
3032{
3033 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3034}
3035
Eric Andersencb57d552001-06-28 07:25:16 +00003036/*
3037 * TODO - sort output
3038 */
Eric Andersenc470f442003-07-28 09:56:35 +00003039static int
3040aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003041{
3042 char *n, *v;
3043 int ret = 0;
3044 struct alias *ap;
3045
3046 if (argc == 1) {
3047 int i;
3048
3049 for (i = 0; i < ATABSIZE; i++)
3050 for (ap = atab[i]; ap; ap = ap->next) {
3051 printalias(ap);
3052 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003053 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003054 }
3055 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003056 v = strchr(n+1, '=');
3057 if (v == NULL) { /* n+1: funny ksh stuff */
3058 ap = *__lookupalias(n);
3059 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003060 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003061 ret = 1;
3062 } else
3063 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003064 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003065 *v++ = '\0';
3066 setalias(n, v);
3067 }
3068 }
3069
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003070 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003071}
3072
Eric Andersenc470f442003-07-28 09:56:35 +00003073static int
3074unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003075{
3076 int i;
3077
3078 while ((i = nextopt("a")) != '\0') {
3079 if (i == 'a') {
3080 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003081 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003082 }
3083 }
3084 for (i = 0; *argptr; argptr++) {
3085 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003086 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003087 i = 1;
3088 }
3089 }
3090
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003091 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003092}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003093
Denis Vlasenko131ae172007-02-18 13:00:19 +00003094#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003095
Eric Andersenc470f442003-07-28 09:56:35 +00003096
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003097/* ============ jobs.c */
3098
3099/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3100#define FORK_FG 0
3101#define FORK_BG 1
3102#define FORK_NOJOB 2
3103
3104/* mode flags for showjob(s) */
3105#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3106#define SHOW_PID 0x04 /* include process pid */
3107#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3108
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003109/*
3110 * A job structure contains information about a job. A job is either a
3111 * single process or a set of processes contained in a pipeline. In the
3112 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3113 * array of pids.
3114 */
3115
3116struct procstat {
3117 pid_t pid; /* process id */
3118 int status; /* last process status from wait() */
3119 char *cmd; /* text of command being run */
3120};
3121
3122struct job {
3123 struct procstat ps0; /* status of process */
3124 struct procstat *ps; /* status or processes when more than one */
3125#if JOBS
3126 int stopstatus; /* status of a stopped job */
3127#endif
3128 uint32_t
3129 nprocs: 16, /* number of processes */
3130 state: 8,
3131#define JOBRUNNING 0 /* at least one proc running */
3132#define JOBSTOPPED 1 /* all procs are stopped */
3133#define JOBDONE 2 /* all procs are completed */
3134#if JOBS
3135 sigint: 1, /* job was killed by SIGINT */
3136 jobctl: 1, /* job running under job control */
3137#endif
3138 waited: 1, /* true if this entry has been waited for */
3139 used: 1, /* true if this entry is in used */
3140 changed: 1; /* true if status has changed */
3141 struct job *prev_job; /* previous job */
3142};
3143
3144static pid_t backgndpid; /* pid of last background process */
3145static int job_warning; /* user was warned about stopped jobs */
3146#if JOBS
3147static int jobctl; /* true if doing job control */
3148#endif
3149
3150static struct job *makejob(union node *, int);
3151static int forkshell(struct job *, union node *, int);
3152static int waitforjob(struct job *);
3153
3154#if ! JOBS
3155#define setjobctl(on) /* do nothing */
3156#else
3157static void setjobctl(int);
3158static void showjobs(FILE *, int);
3159#endif
3160
3161/*
3162 * Set the signal handler for the specified signal. The routine figures
3163 * out what it should be set to.
3164 */
3165static void
3166setsignal(int signo)
3167{
3168 int action;
3169 char *t, tsig;
3170 struct sigaction act;
3171
3172 t = trap[signo];
3173 if (t == NULL)
3174 action = S_DFL;
3175 else if (*t != '\0')
3176 action = S_CATCH;
3177 else
3178 action = S_IGN;
3179 if (rootshell && action == S_DFL) {
3180 switch (signo) {
3181 case SIGINT:
3182 if (iflag || minusc || sflag == 0)
3183 action = S_CATCH;
3184 break;
3185 case SIGQUIT:
3186#if DEBUG
3187 if (debug)
3188 break;
3189#endif
3190 /* FALLTHROUGH */
3191 case SIGTERM:
3192 if (iflag)
3193 action = S_IGN;
3194 break;
3195#if JOBS
3196 case SIGTSTP:
3197 case SIGTTOU:
3198 if (mflag)
3199 action = S_IGN;
3200 break;
3201#endif
3202 }
3203 }
3204
3205 t = &sigmode[signo - 1];
3206 tsig = *t;
3207 if (tsig == 0) {
3208 /*
3209 * current setting unknown
3210 */
3211 if (sigaction(signo, 0, &act) == -1) {
3212 /*
3213 * Pretend it worked; maybe we should give a warning
3214 * here, but other shells don't. We don't alter
3215 * sigmode, so that we retry every time.
3216 */
3217 return;
3218 }
3219 if (act.sa_handler == SIG_IGN) {
3220 if (mflag
3221 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3222 ) {
3223 tsig = S_IGN; /* don't hard ignore these */
3224 } else
3225 tsig = S_HARD_IGN;
3226 } else {
3227 tsig = S_RESET; /* force to be set */
3228 }
3229 }
3230 if (tsig == S_HARD_IGN || tsig == action)
3231 return;
3232 switch (action) {
3233 case S_CATCH:
3234 act.sa_handler = onsig;
3235 break;
3236 case S_IGN:
3237 act.sa_handler = SIG_IGN;
3238 break;
3239 default:
3240 act.sa_handler = SIG_DFL;
3241 }
3242 *t = action;
3243 act.sa_flags = 0;
3244 sigfillset(&act.sa_mask);
3245 sigaction(signo, &act, 0);
3246}
3247
3248/* mode flags for set_curjob */
3249#define CUR_DELETE 2
3250#define CUR_RUNNING 1
3251#define CUR_STOPPED 0
3252
3253/* mode flags for dowait */
3254#define DOWAIT_NORMAL 0
3255#define DOWAIT_BLOCK 1
3256
3257#if JOBS
3258/* pgrp of shell on invocation */
3259static int initialpgrp;
3260static int ttyfd = -1;
3261#endif
3262/* array of jobs */
3263static struct job *jobtab;
3264/* size of array */
3265static unsigned njobs;
3266/* current job */
3267static struct job *curjob;
3268/* number of presumed living untracked jobs */
3269static int jobless;
3270
3271static void
3272set_curjob(struct job *jp, unsigned mode)
3273{
3274 struct job *jp1;
3275 struct job **jpp, **curp;
3276
3277 /* first remove from list */
3278 jpp = curp = &curjob;
3279 do {
3280 jp1 = *jpp;
3281 if (jp1 == jp)
3282 break;
3283 jpp = &jp1->prev_job;
3284 } while (1);
3285 *jpp = jp1->prev_job;
3286
3287 /* Then re-insert in correct position */
3288 jpp = curp;
3289 switch (mode) {
3290 default:
3291#if DEBUG
3292 abort();
3293#endif
3294 case CUR_DELETE:
3295 /* job being deleted */
3296 break;
3297 case CUR_RUNNING:
3298 /* newly created job or backgrounded job,
3299 put after all stopped jobs. */
3300 do {
3301 jp1 = *jpp;
3302#if JOBS
3303 if (!jp1 || jp1->state != JOBSTOPPED)
3304#endif
3305 break;
3306 jpp = &jp1->prev_job;
3307 } while (1);
3308 /* FALLTHROUGH */
3309#if JOBS
3310 case CUR_STOPPED:
3311#endif
3312 /* newly stopped job - becomes curjob */
3313 jp->prev_job = *jpp;
3314 *jpp = jp;
3315 break;
3316 }
3317}
3318
3319#if JOBS || DEBUG
3320static int
3321jobno(const struct job *jp)
3322{
3323 return jp - jobtab + 1;
3324}
3325#endif
3326
3327/*
3328 * Convert a job name to a job structure.
3329 */
3330static struct job *
3331getjob(const char *name, int getctl)
3332{
3333 struct job *jp;
3334 struct job *found;
3335 const char *err_msg = "No such job: %s";
3336 unsigned num;
3337 int c;
3338 const char *p;
3339 char *(*match)(const char *, const char *);
3340
3341 jp = curjob;
3342 p = name;
3343 if (!p)
3344 goto currentjob;
3345
3346 if (*p != '%')
3347 goto err;
3348
3349 c = *++p;
3350 if (!c)
3351 goto currentjob;
3352
3353 if (!p[1]) {
3354 if (c == '+' || c == '%') {
3355 currentjob:
3356 err_msg = "No current job";
3357 goto check;
3358 }
3359 if (c == '-') {
3360 if (jp)
3361 jp = jp->prev_job;
3362 err_msg = "No previous job";
3363 check:
3364 if (!jp)
3365 goto err;
3366 goto gotit;
3367 }
3368 }
3369
3370 if (is_number(p)) {
3371 num = atoi(p);
3372 if (num < njobs) {
3373 jp = jobtab + num - 1;
3374 if (jp->used)
3375 goto gotit;
3376 goto err;
3377 }
3378 }
3379
3380 match = prefix;
3381 if (*p == '?') {
3382 match = strstr;
3383 p++;
3384 }
3385
3386 found = 0;
3387 while (1) {
3388 if (!jp)
3389 goto err;
3390 if (match(jp->ps[0].cmd, p)) {
3391 if (found)
3392 goto err;
3393 found = jp;
3394 err_msg = "%s: ambiguous";
3395 }
3396 jp = jp->prev_job;
3397 }
3398
3399 gotit:
3400#if JOBS
3401 err_msg = "job %s not created under job control";
3402 if (getctl && jp->jobctl == 0)
3403 goto err;
3404#endif
3405 return jp;
3406 err:
3407 ash_msg_and_raise_error(err_msg, name);
3408}
3409
3410/*
3411 * Mark a job structure as unused.
3412 */
3413static void
3414freejob(struct job *jp)
3415{
3416 struct procstat *ps;
3417 int i;
3418
3419 INT_OFF;
3420 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3421 if (ps->cmd != nullstr)
3422 free(ps->cmd);
3423 }
3424 if (jp->ps != &jp->ps0)
3425 free(jp->ps);
3426 jp->used = 0;
3427 set_curjob(jp, CUR_DELETE);
3428 INT_ON;
3429}
3430
3431#if JOBS
3432static void
3433xtcsetpgrp(int fd, pid_t pgrp)
3434{
3435 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003436 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003437}
3438
3439/*
3440 * Turn job control on and off.
3441 *
3442 * Note: This code assumes that the third arg to ioctl is a character
3443 * pointer, which is true on Berkeley systems but not System V. Since
3444 * System V doesn't have job control yet, this isn't a problem now.
3445 *
3446 * Called with interrupts off.
3447 */
3448static void
3449setjobctl(int on)
3450{
3451 int fd;
3452 int pgrp;
3453
3454 if (on == jobctl || rootshell == 0)
3455 return;
3456 if (on) {
3457 int ofd;
3458 ofd = fd = open(_PATH_TTY, O_RDWR);
3459 if (fd < 0) {
3460 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3461 * That sometimes helps to acquire controlling tty.
3462 * Obviously, a workaround for bugs when someone
3463 * failed to provide a controlling tty to bash! :) */
3464 fd += 3;
3465 while (!isatty(fd) && --fd >= 0)
3466 ;
3467 }
3468 fd = fcntl(fd, F_DUPFD, 10);
3469 close(ofd);
3470 if (fd < 0)
3471 goto out;
3472 fcntl(fd, F_SETFD, FD_CLOEXEC);
3473 do { /* while we are in the background */
3474 pgrp = tcgetpgrp(fd);
3475 if (pgrp < 0) {
3476 out:
3477 ash_msg("can't access tty; job control turned off");
3478 mflag = on = 0;
3479 goto close;
3480 }
3481 if (pgrp == getpgrp())
3482 break;
3483 killpg(0, SIGTTIN);
3484 } while (1);
3485 initialpgrp = pgrp;
3486
3487 setsignal(SIGTSTP);
3488 setsignal(SIGTTOU);
3489 setsignal(SIGTTIN);
3490 pgrp = rootpid;
3491 setpgid(0, pgrp);
3492 xtcsetpgrp(fd, pgrp);
3493 } else {
3494 /* turning job control off */
3495 fd = ttyfd;
3496 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003497 /* was xtcsetpgrp, but this can make exiting ash
3498 * with pty already deleted loop forever */
3499 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003500 setpgid(0, pgrp);
3501 setsignal(SIGTSTP);
3502 setsignal(SIGTTOU);
3503 setsignal(SIGTTIN);
3504 close:
3505 close(fd);
3506 fd = -1;
3507 }
3508 ttyfd = fd;
3509 jobctl = on;
3510}
3511
3512static int
3513killcmd(int argc, char **argv)
3514{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003515 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3516 int i = 1;
3517 do {
3518 if (argv[i][0] == '%') {
3519 struct job *jp = getjob(argv[i], 0);
3520 unsigned pid = jp->ps[0].pid;
3521 /* Enough space for ' -NNN<nul>' */
3522 argv[i] = alloca(sizeof(int)*3 + 3);
3523 /* kill_main has matching code to expect
3524 * leading space. Needed to not confuse
3525 * negative pids with "kill -SIGNAL_NO" syntax */
3526 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003527 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003528 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003529 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003530 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003531}
3532
3533static void
3534showpipe(struct job *jp, FILE *out)
3535{
3536 struct procstat *sp;
3537 struct procstat *spend;
3538
3539 spend = jp->ps + jp->nprocs;
3540 for (sp = jp->ps + 1; sp < spend; sp++)
3541 fprintf(out, " | %s", sp->cmd);
3542 outcslow('\n', out);
3543 flush_stdout_stderr();
3544}
3545
3546
3547static int
3548restartjob(struct job *jp, int mode)
3549{
3550 struct procstat *ps;
3551 int i;
3552 int status;
3553 pid_t pgid;
3554
3555 INT_OFF;
3556 if (jp->state == JOBDONE)
3557 goto out;
3558 jp->state = JOBRUNNING;
3559 pgid = jp->ps->pid;
3560 if (mode == FORK_FG)
3561 xtcsetpgrp(ttyfd, pgid);
3562 killpg(pgid, SIGCONT);
3563 ps = jp->ps;
3564 i = jp->nprocs;
3565 do {
3566 if (WIFSTOPPED(ps->status)) {
3567 ps->status = -1;
3568 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003569 ps++;
3570 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003571 out:
3572 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3573 INT_ON;
3574 return status;
3575}
3576
3577static int
3578fg_bgcmd(int argc, char **argv)
3579{
3580 struct job *jp;
3581 FILE *out;
3582 int mode;
3583 int retval;
3584
3585 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3586 nextopt(nullstr);
3587 argv = argptr;
3588 out = stdout;
3589 do {
3590 jp = getjob(*argv, 1);
3591 if (mode == FORK_BG) {
3592 set_curjob(jp, CUR_RUNNING);
3593 fprintf(out, "[%d] ", jobno(jp));
3594 }
3595 outstr(jp->ps->cmd, out);
3596 showpipe(jp, out);
3597 retval = restartjob(jp, mode);
3598 } while (*argv && *++argv);
3599 return retval;
3600}
3601#endif
3602
3603static int
3604sprint_status(char *s, int status, int sigonly)
3605{
3606 int col;
3607 int st;
3608
3609 col = 0;
3610 if (!WIFEXITED(status)) {
3611#if JOBS
3612 if (WIFSTOPPED(status))
3613 st = WSTOPSIG(status);
3614 else
3615#endif
3616 st = WTERMSIG(status);
3617 if (sigonly) {
3618 if (st == SIGINT || st == SIGPIPE)
3619 goto out;
3620#if JOBS
3621 if (WIFSTOPPED(status))
3622 goto out;
3623#endif
3624 }
3625 st &= 0x7f;
3626 col = fmtstr(s, 32, strsignal(st));
3627 if (WCOREDUMP(status)) {
3628 col += fmtstr(s + col, 16, " (core dumped)");
3629 }
3630 } else if (!sigonly) {
3631 st = WEXITSTATUS(status);
3632 if (st)
3633 col = fmtstr(s, 16, "Done(%d)", st);
3634 else
3635 col = fmtstr(s, 16, "Done");
3636 }
3637 out:
3638 return col;
3639}
3640
3641/*
3642 * Do a wait system call. If job control is compiled in, we accept
3643 * stopped processes. If block is zero, we return a value of zero
3644 * rather than blocking.
3645 *
3646 * System V doesn't have a non-blocking wait system call. It does
3647 * have a SIGCLD signal that is sent to a process when one of it's
3648 * children dies. The obvious way to use SIGCLD would be to install
3649 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3650 * was received, and have waitproc bump another counter when it got
3651 * the status of a process. Waitproc would then know that a wait
3652 * system call would not block if the two counters were different.
3653 * This approach doesn't work because if a process has children that
3654 * have not been waited for, System V will send it a SIGCLD when it
3655 * installs a signal handler for SIGCLD. What this means is that when
3656 * a child exits, the shell will be sent SIGCLD signals continuously
3657 * until is runs out of stack space, unless it does a wait call before
3658 * restoring the signal handler. The code below takes advantage of
3659 * this (mis)feature by installing a signal handler for SIGCLD and
3660 * then checking to see whether it was called. If there are any
3661 * children to be waited for, it will be.
3662 *
3663 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3664 * waits at all. In this case, the user will not be informed when
3665 * a background process until the next time she runs a real program
3666 * (as opposed to running a builtin command or just typing return),
3667 * and the jobs command may give out of date information.
3668 */
3669static int
3670waitproc(int block, int *status)
3671{
3672 int flags = 0;
3673
3674#if JOBS
3675 if (jobctl)
3676 flags |= WUNTRACED;
3677#endif
3678 if (block == 0)
3679 flags |= WNOHANG;
3680 return wait3(status, flags, (struct rusage *)NULL);
3681}
3682
3683/*
3684 * Wait for a process to terminate.
3685 */
3686static int
3687dowait(int block, struct job *job)
3688{
3689 int pid;
3690 int status;
3691 struct job *jp;
3692 struct job *thisjob;
3693 int state;
3694
3695 TRACE(("dowait(%d) called\n", block));
3696 pid = waitproc(block, &status);
3697 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3698 if (pid <= 0)
3699 return pid;
3700 INT_OFF;
3701 thisjob = NULL;
3702 for (jp = curjob; jp; jp = jp->prev_job) {
3703 struct procstat *sp;
3704 struct procstat *spend;
3705 if (jp->state == JOBDONE)
3706 continue;
3707 state = JOBDONE;
3708 spend = jp->ps + jp->nprocs;
3709 sp = jp->ps;
3710 do {
3711 if (sp->pid == pid) {
3712 TRACE(("Job %d: changing status of proc %d "
3713 "from 0x%x to 0x%x\n",
3714 jobno(jp), pid, sp->status, status));
3715 sp->status = status;
3716 thisjob = jp;
3717 }
3718 if (sp->status == -1)
3719 state = JOBRUNNING;
3720#if JOBS
3721 if (state == JOBRUNNING)
3722 continue;
3723 if (WIFSTOPPED(sp->status)) {
3724 jp->stopstatus = sp->status;
3725 state = JOBSTOPPED;
3726 }
3727#endif
3728 } while (++sp < spend);
3729 if (thisjob)
3730 goto gotjob;
3731 }
3732#if JOBS
3733 if (!WIFSTOPPED(status))
3734#endif
3735
3736 jobless--;
3737 goto out;
3738
3739 gotjob:
3740 if (state != JOBRUNNING) {
3741 thisjob->changed = 1;
3742
3743 if (thisjob->state != state) {
3744 TRACE(("Job %d: changing state from %d to %d\n",
3745 jobno(thisjob), thisjob->state, state));
3746 thisjob->state = state;
3747#if JOBS
3748 if (state == JOBSTOPPED) {
3749 set_curjob(thisjob, CUR_STOPPED);
3750 }
3751#endif
3752 }
3753 }
3754
3755 out:
3756 INT_ON;
3757
3758 if (thisjob && thisjob == job) {
3759 char s[48 + 1];
3760 int len;
3761
3762 len = sprint_status(s, status, 1);
3763 if (len) {
3764 s[len] = '\n';
3765 s[len + 1] = 0;
3766 out2str(s);
3767 }
3768 }
3769 return pid;
3770}
3771
3772#if JOBS
3773static void
3774showjob(FILE *out, struct job *jp, int mode)
3775{
3776 struct procstat *ps;
3777 struct procstat *psend;
3778 int col;
3779 int indent;
3780 char s[80];
3781
3782 ps = jp->ps;
3783
3784 if (mode & SHOW_PGID) {
3785 /* just output process (group) id of pipeline */
3786 fprintf(out, "%d\n", ps->pid);
3787 return;
3788 }
3789
3790 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3791 indent = col;
3792
3793 if (jp == curjob)
3794 s[col - 2] = '+';
3795 else if (curjob && jp == curjob->prev_job)
3796 s[col - 2] = '-';
3797
3798 if (mode & SHOW_PID)
3799 col += fmtstr(s + col, 16, "%d ", ps->pid);
3800
3801 psend = ps + jp->nprocs;
3802
3803 if (jp->state == JOBRUNNING) {
3804 strcpy(s + col, "Running");
3805 col += sizeof("Running") - 1;
3806 } else {
3807 int status = psend[-1].status;
3808 if (jp->state == JOBSTOPPED)
3809 status = jp->stopstatus;
3810 col += sprint_status(s + col, status, 0);
3811 }
3812
3813 goto start;
3814
3815 do {
3816 /* for each process */
3817 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
3818 start:
3819 fprintf(out, "%s%*c%s",
3820 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3821 );
3822 if (!(mode & SHOW_PID)) {
3823 showpipe(jp, out);
3824 break;
3825 }
3826 if (++ps == psend) {
3827 outcslow('\n', out);
3828 break;
3829 }
3830 } while (1);
3831
3832 jp->changed = 0;
3833
3834 if (jp->state == JOBDONE) {
3835 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3836 freejob(jp);
3837 }
3838}
3839
3840static int
3841jobscmd(int argc, char **argv)
3842{
3843 int mode, m;
3844 FILE *out;
3845
3846 mode = 0;
3847 while ((m = nextopt("lp"))) {
3848 if (m == 'l')
3849 mode = SHOW_PID;
3850 else
3851 mode = SHOW_PGID;
3852 }
3853
3854 out = stdout;
3855 argv = argptr;
3856 if (*argv) {
3857 do
3858 showjob(out, getjob(*argv,0), mode);
3859 while (*++argv);
3860 } else
3861 showjobs(out, mode);
3862
3863 return 0;
3864}
3865
3866/*
3867 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3868 * statuses have changed since the last call to showjobs.
3869 */
3870static void
3871showjobs(FILE *out, int mode)
3872{
3873 struct job *jp;
3874
3875 TRACE(("showjobs(%x) called\n", mode));
3876
3877 /* If not even one one job changed, there is nothing to do */
3878 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3879 continue;
3880
3881 for (jp = curjob; jp; jp = jp->prev_job) {
3882 if (!(mode & SHOW_CHANGED) || jp->changed)
3883 showjob(out, jp, mode);
3884 }
3885}
3886#endif /* JOBS */
3887
3888static int
3889getstatus(struct job *job)
3890{
3891 int status;
3892 int retval;
3893
3894 status = job->ps[job->nprocs - 1].status;
3895 retval = WEXITSTATUS(status);
3896 if (!WIFEXITED(status)) {
3897#if JOBS
3898 retval = WSTOPSIG(status);
3899 if (!WIFSTOPPED(status))
3900#endif
3901 {
3902 /* XXX: limits number of signals */
3903 retval = WTERMSIG(status);
3904#if JOBS
3905 if (retval == SIGINT)
3906 job->sigint = 1;
3907#endif
3908 }
3909 retval += 128;
3910 }
3911 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3912 jobno(job), job->nprocs, status, retval));
3913 return retval;
3914}
3915
3916static int
3917waitcmd(int argc, char **argv)
3918{
3919 struct job *job;
3920 int retval;
3921 struct job *jp;
3922
3923 EXSIGON;
3924
3925 nextopt(nullstr);
3926 retval = 0;
3927
3928 argv = argptr;
3929 if (!*argv) {
3930 /* wait for all jobs */
3931 for (;;) {
3932 jp = curjob;
3933 while (1) {
3934 if (!jp) {
3935 /* no running procs */
3936 goto out;
3937 }
3938 if (jp->state == JOBRUNNING)
3939 break;
3940 jp->waited = 1;
3941 jp = jp->prev_job;
3942 }
3943 dowait(DOWAIT_BLOCK, 0);
3944 }
3945 }
3946
3947 retval = 127;
3948 do {
3949 if (**argv != '%') {
3950 pid_t pid = number(*argv);
3951 job = curjob;
3952 goto start;
3953 do {
3954 if (job->ps[job->nprocs - 1].pid == pid)
3955 break;
3956 job = job->prev_job;
3957 start:
3958 if (!job)
3959 goto repeat;
3960 } while (1);
3961 } else
3962 job = getjob(*argv, 0);
3963 /* loop until process terminated or stopped */
3964 while (job->state == JOBRUNNING)
3965 dowait(DOWAIT_BLOCK, 0);
3966 job->waited = 1;
3967 retval = getstatus(job);
3968 repeat:
3969 ;
3970 } while (*++argv);
3971
3972 out:
3973 return retval;
3974}
3975
3976static struct job *
3977growjobtab(void)
3978{
3979 size_t len;
3980 ptrdiff_t offset;
3981 struct job *jp, *jq;
3982
3983 len = njobs * sizeof(*jp);
3984 jq = jobtab;
3985 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
3986
3987 offset = (char *)jp - (char *)jq;
3988 if (offset) {
3989 /* Relocate pointers */
3990 size_t l = len;
3991
3992 jq = (struct job *)((char *)jq + l);
3993 while (l) {
3994 l -= sizeof(*jp);
3995 jq--;
3996#define joff(p) ((struct job *)((char *)(p) + l))
3997#define jmove(p) (p) = (void *)((char *)(p) + offset)
3998 if (joff(jp)->ps == &jq->ps0)
3999 jmove(joff(jp)->ps);
4000 if (joff(jp)->prev_job)
4001 jmove(joff(jp)->prev_job);
4002 }
4003 if (curjob)
4004 jmove(curjob);
4005#undef joff
4006#undef jmove
4007 }
4008
4009 njobs += 4;
4010 jobtab = jp;
4011 jp = (struct job *)((char *)jp + len);
4012 jq = jp + 3;
4013 do {
4014 jq->used = 0;
4015 } while (--jq >= jp);
4016 return jp;
4017}
4018
4019/*
4020 * Return a new job structure.
4021 * Called with interrupts off.
4022 */
4023static struct job *
4024makejob(union node *node, int nprocs)
4025{
4026 int i;
4027 struct job *jp;
4028
4029 for (i = njobs, jp = jobtab; ; jp++) {
4030 if (--i < 0) {
4031 jp = growjobtab();
4032 break;
4033 }
4034 if (jp->used == 0)
4035 break;
4036 if (jp->state != JOBDONE || !jp->waited)
4037 continue;
4038#if JOBS
4039 if (jobctl)
4040 continue;
4041#endif
4042 freejob(jp);
4043 break;
4044 }
4045 memset(jp, 0, sizeof(*jp));
4046#if JOBS
4047 if (jobctl)
4048 jp->jobctl = 1;
4049#endif
4050 jp->prev_job = curjob;
4051 curjob = jp;
4052 jp->used = 1;
4053 jp->ps = &jp->ps0;
4054 if (nprocs > 1) {
4055 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4056 }
4057 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4058 jobno(jp)));
4059 return jp;
4060}
4061
4062#if JOBS
4063/*
4064 * Return a string identifying a command (to be printed by the
4065 * jobs command).
4066 */
4067static char *cmdnextc;
4068
4069static void
4070cmdputs(const char *s)
4071{
4072 const char *p, *str;
4073 char c, cc[2] = " ";
4074 char *nextc;
4075 int subtype = 0;
4076 int quoted = 0;
4077 static const char vstype[VSTYPE + 1][4] = {
4078 "", "}", "-", "+", "?", "=",
4079 "%", "%%", "#", "##"
4080 };
4081
4082 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4083 p = s;
4084 while ((c = *p++) != 0) {
4085 str = 0;
4086 switch (c) {
4087 case CTLESC:
4088 c = *p++;
4089 break;
4090 case CTLVAR:
4091 subtype = *p++;
4092 if ((subtype & VSTYPE) == VSLENGTH)
4093 str = "${#";
4094 else
4095 str = "${";
4096 if (!(subtype & VSQUOTE) == !(quoted & 1))
4097 goto dostr;
4098 quoted ^= 1;
4099 c = '"';
4100 break;
4101 case CTLENDVAR:
4102 str = "\"}" + !(quoted & 1);
4103 quoted >>= 1;
4104 subtype = 0;
4105 goto dostr;
4106 case CTLBACKQ:
4107 str = "$(...)";
4108 goto dostr;
4109 case CTLBACKQ+CTLQUOTE:
4110 str = "\"$(...)\"";
4111 goto dostr;
4112#if ENABLE_ASH_MATH_SUPPORT
4113 case CTLARI:
4114 str = "$((";
4115 goto dostr;
4116 case CTLENDARI:
4117 str = "))";
4118 goto dostr;
4119#endif
4120 case CTLQUOTEMARK:
4121 quoted ^= 1;
4122 c = '"';
4123 break;
4124 case '=':
4125 if (subtype == 0)
4126 break;
4127 if ((subtype & VSTYPE) != VSNORMAL)
4128 quoted <<= 1;
4129 str = vstype[subtype & VSTYPE];
4130 if (subtype & VSNUL)
4131 c = ':';
4132 else
4133 goto checkstr;
4134 break;
4135 case '\'':
4136 case '\\':
4137 case '"':
4138 case '$':
4139 /* These can only happen inside quotes */
4140 cc[0] = c;
4141 str = cc;
4142 c = '\\';
4143 break;
4144 default:
4145 break;
4146 }
4147 USTPUTC(c, nextc);
4148 checkstr:
4149 if (!str)
4150 continue;
4151 dostr:
4152 while ((c = *str++)) {
4153 USTPUTC(c, nextc);
4154 }
4155 }
4156 if (quoted & 1) {
4157 USTPUTC('"', nextc);
4158 }
4159 *nextc = 0;
4160 cmdnextc = nextc;
4161}
4162
4163/* cmdtxt() and cmdlist() call each other */
4164static void cmdtxt(union node *n);
4165
4166static void
4167cmdlist(union node *np, int sep)
4168{
4169 for (; np; np = np->narg.next) {
4170 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004171 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004172 cmdtxt(np);
4173 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004174 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175 }
4176}
4177
4178static void
4179cmdtxt(union node *n)
4180{
4181 union node *np;
4182 struct nodelist *lp;
4183 const char *p;
4184 char s[2];
4185
4186 if (!n)
4187 return;
4188 switch (n->type) {
4189 default:
4190#if DEBUG
4191 abort();
4192#endif
4193 case NPIPE:
4194 lp = n->npipe.cmdlist;
4195 for (;;) {
4196 cmdtxt(lp->n);
4197 lp = lp->next;
4198 if (!lp)
4199 break;
4200 cmdputs(" | ");
4201 }
4202 break;
4203 case NSEMI:
4204 p = "; ";
4205 goto binop;
4206 case NAND:
4207 p = " && ";
4208 goto binop;
4209 case NOR:
4210 p = " || ";
4211 binop:
4212 cmdtxt(n->nbinary.ch1);
4213 cmdputs(p);
4214 n = n->nbinary.ch2;
4215 goto donode;
4216 case NREDIR:
4217 case NBACKGND:
4218 n = n->nredir.n;
4219 goto donode;
4220 case NNOT:
4221 cmdputs("!");
4222 n = n->nnot.com;
4223 donode:
4224 cmdtxt(n);
4225 break;
4226 case NIF:
4227 cmdputs("if ");
4228 cmdtxt(n->nif.test);
4229 cmdputs("; then ");
4230 n = n->nif.ifpart;
4231 if (n->nif.elsepart) {
4232 cmdtxt(n);
4233 cmdputs("; else ");
4234 n = n->nif.elsepart;
4235 }
4236 p = "; fi";
4237 goto dotail;
4238 case NSUBSHELL:
4239 cmdputs("(");
4240 n = n->nredir.n;
4241 p = ")";
4242 goto dotail;
4243 case NWHILE:
4244 p = "while ";
4245 goto until;
4246 case NUNTIL:
4247 p = "until ";
4248 until:
4249 cmdputs(p);
4250 cmdtxt(n->nbinary.ch1);
4251 n = n->nbinary.ch2;
4252 p = "; done";
4253 dodo:
4254 cmdputs("; do ");
4255 dotail:
4256 cmdtxt(n);
4257 goto dotail2;
4258 case NFOR:
4259 cmdputs("for ");
4260 cmdputs(n->nfor.var);
4261 cmdputs(" in ");
4262 cmdlist(n->nfor.args, 1);
4263 n = n->nfor.body;
4264 p = "; done";
4265 goto dodo;
4266 case NDEFUN:
4267 cmdputs(n->narg.text);
4268 p = "() { ... }";
4269 goto dotail2;
4270 case NCMD:
4271 cmdlist(n->ncmd.args, 1);
4272 cmdlist(n->ncmd.redirect, 0);
4273 break;
4274 case NARG:
4275 p = n->narg.text;
4276 dotail2:
4277 cmdputs(p);
4278 break;
4279 case NHERE:
4280 case NXHERE:
4281 p = "<<...";
4282 goto dotail2;
4283 case NCASE:
4284 cmdputs("case ");
4285 cmdputs(n->ncase.expr->narg.text);
4286 cmdputs(" in ");
4287 for (np = n->ncase.cases; np; np = np->nclist.next) {
4288 cmdtxt(np->nclist.pattern);
4289 cmdputs(") ");
4290 cmdtxt(np->nclist.body);
4291 cmdputs(";; ");
4292 }
4293 p = "esac";
4294 goto dotail2;
4295 case NTO:
4296 p = ">";
4297 goto redir;
4298 case NCLOBBER:
4299 p = ">|";
4300 goto redir;
4301 case NAPPEND:
4302 p = ">>";
4303 goto redir;
4304 case NTOFD:
4305 p = ">&";
4306 goto redir;
4307 case NFROM:
4308 p = "<";
4309 goto redir;
4310 case NFROMFD:
4311 p = "<&";
4312 goto redir;
4313 case NFROMTO:
4314 p = "<>";
4315 redir:
4316 s[0] = n->nfile.fd + '0';
4317 s[1] = '\0';
4318 cmdputs(s);
4319 cmdputs(p);
4320 if (n->type == NTOFD || n->type == NFROMFD) {
4321 s[0] = n->ndup.dupfd + '0';
4322 p = s;
4323 goto dotail2;
4324 }
4325 n = n->nfile.fname;
4326 goto donode;
4327 }
4328}
4329
4330static char *
4331commandtext(union node *n)
4332{
4333 char *name;
4334
4335 STARTSTACKSTR(cmdnextc);
4336 cmdtxt(n);
4337 name = stackblock();
4338 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4339 name, cmdnextc, cmdnextc));
4340 return ckstrdup(name);
4341}
4342#endif /* JOBS */
4343
4344/*
4345 * Fork off a subshell. If we are doing job control, give the subshell its
4346 * own process group. Jp is a job structure that the job is to be added to.
4347 * N is the command that will be evaluated by the child. Both jp and n may
4348 * be NULL. The mode parameter can be one of the following:
4349 * FORK_FG - Fork off a foreground process.
4350 * FORK_BG - Fork off a background process.
4351 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4352 * process group even if job control is on.
4353 *
4354 * When job control is turned off, background processes have their standard
4355 * input redirected to /dev/null (except for the second and later processes
4356 * in a pipeline).
4357 *
4358 * Called with interrupts off.
4359 */
4360/*
4361 * Clear traps on a fork.
4362 */
4363static void
4364clear_traps(void)
4365{
4366 char **tp;
4367
4368 for (tp = trap; tp < &trap[NSIG]; tp++) {
4369 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4370 INT_OFF;
4371 free(*tp);
4372 *tp = NULL;
4373 if (tp != &trap[0])
4374 setsignal(tp - trap);
4375 INT_ON;
4376 }
4377 }
4378}
4379/* lives far away from here, needed for forkchild */
4380static void closescript(void);
4381static void
4382forkchild(struct job *jp, union node *n, int mode)
4383{
4384 int oldlvl;
4385
4386 TRACE(("Child shell %d\n", getpid()));
4387 oldlvl = shlvl;
4388 shlvl++;
4389
4390 closescript();
4391 clear_traps();
4392#if JOBS
4393 /* do job control only in root shell */
4394 jobctl = 0;
4395 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4396 pid_t pgrp;
4397
4398 if (jp->nprocs == 0)
4399 pgrp = getpid();
4400 else
4401 pgrp = jp->ps[0].pid;
4402 /* This can fail because we are doing it in the parent also */
4403 (void)setpgid(0, pgrp);
4404 if (mode == FORK_FG)
4405 xtcsetpgrp(ttyfd, pgrp);
4406 setsignal(SIGTSTP);
4407 setsignal(SIGTTOU);
4408 } else
4409#endif
4410 if (mode == FORK_BG) {
4411 ignoresig(SIGINT);
4412 ignoresig(SIGQUIT);
4413 if (jp->nprocs == 0) {
4414 close(0);
4415 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004416 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004417 }
4418 }
4419 if (!oldlvl && iflag) {
4420 setsignal(SIGINT);
4421 setsignal(SIGQUIT);
4422 setsignal(SIGTERM);
4423 }
4424 for (jp = curjob; jp; jp = jp->prev_job)
4425 freejob(jp);
4426 jobless = 0;
4427}
4428
4429static void
4430forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4431{
4432 TRACE(("In parent shell: child = %d\n", pid));
4433 if (!jp) {
4434 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4435 jobless++;
4436 return;
4437 }
4438#if JOBS
4439 if (mode != FORK_NOJOB && jp->jobctl) {
4440 int pgrp;
4441
4442 if (jp->nprocs == 0)
4443 pgrp = pid;
4444 else
4445 pgrp = jp->ps[0].pid;
4446 /* This can fail because we are doing it in the child also */
4447 setpgid(pid, pgrp);
4448 }
4449#endif
4450 if (mode == FORK_BG) {
4451 backgndpid = pid; /* set $! */
4452 set_curjob(jp, CUR_RUNNING);
4453 }
4454 if (jp) {
4455 struct procstat *ps = &jp->ps[jp->nprocs++];
4456 ps->pid = pid;
4457 ps->status = -1;
4458 ps->cmd = nullstr;
4459#if JOBS
4460 if (jobctl && n)
4461 ps->cmd = commandtext(n);
4462#endif
4463 }
4464}
4465
4466static int
4467forkshell(struct job *jp, union node *n, int mode)
4468{
4469 int pid;
4470
4471 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4472 pid = fork();
4473 if (pid < 0) {
4474 TRACE(("Fork failed, errno=%d", errno));
4475 if (jp)
4476 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004477 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004478 }
4479 if (pid == 0)
4480 forkchild(jp, n, mode);
4481 else
4482 forkparent(jp, n, mode, pid);
4483 return pid;
4484}
4485
4486/*
4487 * Wait for job to finish.
4488 *
4489 * Under job control we have the problem that while a child process is
4490 * running interrupts generated by the user are sent to the child but not
4491 * to the shell. This means that an infinite loop started by an inter-
4492 * active user may be hard to kill. With job control turned off, an
4493 * interactive user may place an interactive program inside a loop. If
4494 * the interactive program catches interrupts, the user doesn't want
4495 * these interrupts to also abort the loop. The approach we take here
4496 * is to have the shell ignore interrupt signals while waiting for a
4497 * foreground process to terminate, and then send itself an interrupt
4498 * signal if the child process was terminated by an interrupt signal.
4499 * Unfortunately, some programs want to do a bit of cleanup and then
4500 * exit on interrupt; unless these processes terminate themselves by
4501 * sending a signal to themselves (instead of calling exit) they will
4502 * confuse this approach.
4503 *
4504 * Called with interrupts off.
4505 */
4506static int
4507waitforjob(struct job *jp)
4508{
4509 int st;
4510
4511 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4512 while (jp->state == JOBRUNNING) {
4513 dowait(DOWAIT_BLOCK, jp);
4514 }
4515 st = getstatus(jp);
4516#if JOBS
4517 if (jp->jobctl) {
4518 xtcsetpgrp(ttyfd, rootpid);
4519 /*
4520 * This is truly gross.
4521 * If we're doing job control, then we did a TIOCSPGRP which
4522 * caused us (the shell) to no longer be in the controlling
4523 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4524 * intuit from the subprocess exit status whether a SIGINT
4525 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4526 */
4527 if (jp->sigint)
4528 raise(SIGINT);
4529 }
4530 if (jp->state == JOBDONE)
4531#endif
4532 freejob(jp);
4533 return st;
4534}
4535
4536/*
4537 * return 1 if there are stopped jobs, otherwise 0
4538 */
4539static int
4540stoppedjobs(void)
4541{
4542 struct job *jp;
4543 int retval;
4544
4545 retval = 0;
4546 if (job_warning)
4547 goto out;
4548 jp = curjob;
4549 if (jp && jp->state == JOBSTOPPED) {
4550 out2str("You have stopped jobs.\n");
4551 job_warning = 2;
4552 retval++;
4553 }
4554 out:
4555 return retval;
4556}
4557
4558
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004559/* ============ redir.c
4560 *
4561 * Code for dealing with input/output redirection.
4562 */
4563
4564#define EMPTY -2 /* marks an unused slot in redirtab */
4565#ifndef PIPE_BUF
4566# define PIPESIZE 4096 /* amount of buffering in a pipe */
4567#else
4568# define PIPESIZE PIPE_BUF
4569#endif
4570
4571/*
4572 * Open a file in noclobber mode.
4573 * The code was copied from bash.
4574 */
4575static int
4576noclobberopen(const char *fname)
4577{
4578 int r, fd;
4579 struct stat finfo, finfo2;
4580
4581 /*
4582 * If the file exists and is a regular file, return an error
4583 * immediately.
4584 */
4585 r = stat(fname, &finfo);
4586 if (r == 0 && S_ISREG(finfo.st_mode)) {
4587 errno = EEXIST;
4588 return -1;
4589 }
4590
4591 /*
4592 * If the file was not present (r != 0), make sure we open it
4593 * exclusively so that if it is created before we open it, our open
4594 * will fail. Make sure that we do not truncate an existing file.
4595 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4596 * file was not a regular file, we leave O_EXCL off.
4597 */
4598 if (r != 0)
4599 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4600 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4601
4602 /* If the open failed, return the file descriptor right away. */
4603 if (fd < 0)
4604 return fd;
4605
4606 /*
4607 * OK, the open succeeded, but the file may have been changed from a
4608 * non-regular file to a regular file between the stat and the open.
4609 * We are assuming that the O_EXCL open handles the case where FILENAME
4610 * did not exist and is symlinked to an existing file between the stat
4611 * and open.
4612 */
4613
4614 /*
4615 * If we can open it and fstat the file descriptor, and neither check
4616 * revealed that it was a regular file, and the file has not been
4617 * replaced, return the file descriptor.
4618 */
4619 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4620 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4621 return fd;
4622
4623 /* The file has been replaced. badness. */
4624 close(fd);
4625 errno = EEXIST;
4626 return -1;
4627}
4628
4629/*
4630 * Handle here documents. Normally we fork off a process to write the
4631 * data to a pipe. If the document is short, we can stuff the data in
4632 * the pipe without forking.
4633 */
4634/* openhere needs this forward reference */
4635static void expandhere(union node *arg, int fd);
4636static int
4637openhere(union node *redir)
4638{
4639 int pip[2];
4640 size_t len = 0;
4641
4642 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004643 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004644 if (redir->type == NHERE) {
4645 len = strlen(redir->nhere.doc->narg.text);
4646 if (len <= PIPESIZE) {
4647 full_write(pip[1], redir->nhere.doc->narg.text, len);
4648 goto out;
4649 }
4650 }
4651 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4652 close(pip[0]);
4653 signal(SIGINT, SIG_IGN);
4654 signal(SIGQUIT, SIG_IGN);
4655 signal(SIGHUP, SIG_IGN);
4656#ifdef SIGTSTP
4657 signal(SIGTSTP, SIG_IGN);
4658#endif
4659 signal(SIGPIPE, SIG_DFL);
4660 if (redir->type == NHERE)
4661 full_write(pip[1], redir->nhere.doc->narg.text, len);
4662 else
4663 expandhere(redir->nhere.doc, pip[1]);
4664 _exit(0);
4665 }
4666 out:
4667 close(pip[1]);
4668 return pip[0];
4669}
4670
4671static int
4672openredirect(union node *redir)
4673{
4674 char *fname;
4675 int f;
4676
4677 switch (redir->nfile.type) {
4678 case NFROM:
4679 fname = redir->nfile.expfname;
4680 f = open(fname, O_RDONLY);
4681 if (f < 0)
4682 goto eopen;
4683 break;
4684 case NFROMTO:
4685 fname = redir->nfile.expfname;
4686 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4687 if (f < 0)
4688 goto ecreate;
4689 break;
4690 case NTO:
4691 /* Take care of noclobber mode. */
4692 if (Cflag) {
4693 fname = redir->nfile.expfname;
4694 f = noclobberopen(fname);
4695 if (f < 0)
4696 goto ecreate;
4697 break;
4698 }
4699 /* FALLTHROUGH */
4700 case NCLOBBER:
4701 fname = redir->nfile.expfname;
4702 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4703 if (f < 0)
4704 goto ecreate;
4705 break;
4706 case NAPPEND:
4707 fname = redir->nfile.expfname;
4708 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4709 if (f < 0)
4710 goto ecreate;
4711 break;
4712 default:
4713#if DEBUG
4714 abort();
4715#endif
4716 /* Fall through to eliminate warning. */
4717 case NTOFD:
4718 case NFROMFD:
4719 f = -1;
4720 break;
4721 case NHERE:
4722 case NXHERE:
4723 f = openhere(redir);
4724 break;
4725 }
4726
4727 return f;
4728 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004729 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004730 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004731 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004732}
4733
4734/*
4735 * Copy a file descriptor to be >= to. Returns -1
4736 * if the source file descriptor is closed, EMPTY if there are no unused
4737 * file descriptors left.
4738 */
4739static int
4740copyfd(int from, int to)
4741{
4742 int newfd;
4743
4744 newfd = fcntl(from, F_DUPFD, to);
4745 if (newfd < 0) {
4746 if (errno == EMFILE)
4747 return EMPTY;
4748 ash_msg_and_raise_error("%d: %m", from);
4749 }
4750 return newfd;
4751}
4752
4753static void
4754dupredirect(union node *redir, int f)
4755{
4756 int fd = redir->nfile.fd;
4757
4758 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4759 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4760 copyfd(redir->ndup.dupfd, fd);
4761 }
4762 return;
4763 }
4764
4765 if (f != fd) {
4766 copyfd(f, fd);
4767 close(f);
4768 }
4769}
4770
4771/*
4772 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4773 * old file descriptors are stashed away so that the redirection can be
4774 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4775 * standard output, and the standard error if it becomes a duplicate of
4776 * stdout, is saved in memory.
4777 */
4778/* flags passed to redirect */
4779#define REDIR_PUSH 01 /* save previous values of file descriptors */
4780#define REDIR_SAVEFD2 03 /* set preverrout */
4781static void
4782redirect(union node *redir, int flags)
4783{
4784 union node *n;
4785 struct redirtab *sv;
4786 int i;
4787 int fd;
4788 int newfd;
4789 int *p;
4790 nullredirs++;
4791 if (!redir) {
4792 return;
4793 }
4794 sv = NULL;
4795 INT_OFF;
4796 if (flags & REDIR_PUSH) {
4797 struct redirtab *q;
4798 q = ckmalloc(sizeof(struct redirtab));
4799 q->next = redirlist;
4800 redirlist = q;
4801 q->nullredirs = nullredirs - 1;
4802 for (i = 0; i < 10; i++)
4803 q->renamed[i] = EMPTY;
4804 nullredirs = 0;
4805 sv = q;
4806 }
4807 n = redir;
4808 do {
4809 fd = n->nfile.fd;
4810 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4811 && n->ndup.dupfd == fd)
4812 continue; /* redirect from/to same file descriptor */
4813
4814 newfd = openredirect(n);
4815 if (fd == newfd)
4816 continue;
4817 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4818 i = fcntl(fd, F_DUPFD, 10);
4819
4820 if (i == -1) {
4821 i = errno;
4822 if (i != EBADF) {
4823 close(newfd);
4824 errno = i;
4825 ash_msg_and_raise_error("%d: %m", fd);
4826 /* NOTREACHED */
4827 }
4828 } else {
4829 *p = i;
4830 close(fd);
4831 }
4832 } else {
4833 close(fd);
4834 }
4835 dupredirect(n, newfd);
4836 } while ((n = n->nfile.next));
4837 INT_ON;
4838 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4839 preverrout_fd = sv->renamed[2];
4840}
4841
4842/*
4843 * Undo the effects of the last redirection.
4844 */
4845static void
4846popredir(int drop)
4847{
4848 struct redirtab *rp;
4849 int i;
4850
4851 if (--nullredirs >= 0)
4852 return;
4853 INT_OFF;
4854 rp = redirlist;
4855 for (i = 0; i < 10; i++) {
4856 if (rp->renamed[i] != EMPTY) {
4857 if (!drop) {
4858 close(i);
4859 copyfd(rp->renamed[i], i);
4860 }
4861 close(rp->renamed[i]);
4862 }
4863 }
4864 redirlist = rp->next;
4865 nullredirs = rp->nullredirs;
4866 free(rp);
4867 INT_ON;
4868}
4869
4870/*
4871 * Undo all redirections. Called on error or interrupt.
4872 */
4873
4874/*
4875 * Discard all saved file descriptors.
4876 */
4877static void
4878clearredir(int drop)
4879{
4880 for (;;) {
4881 nullredirs = 0;
4882 if (!redirlist)
4883 break;
4884 popredir(drop);
4885 }
4886}
4887
4888static int
4889redirectsafe(union node *redir, int flags)
4890{
4891 int err;
4892 volatile int saveint;
4893 struct jmploc *volatile savehandler = exception_handler;
4894 struct jmploc jmploc;
4895
4896 SAVE_INT(saveint);
4897 err = setjmp(jmploc.loc) * 2;
4898 if (!err) {
4899 exception_handler = &jmploc;
4900 redirect(redir, flags);
4901 }
4902 exception_handler = savehandler;
4903 if (err && exception != EXERROR)
4904 longjmp(exception_handler->loc, 1);
4905 RESTORE_INT(saveint);
4906 return err;
4907}
4908
4909
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004910/* ============ Routines to expand arguments to commands
4911 *
4912 * We have to deal with backquotes, shell variables, and file metacharacters.
4913 */
4914
4915/*
4916 * expandarg flags
4917 */
4918#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4919#define EXP_TILDE 0x2 /* do normal tilde expansion */
4920#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4921#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4922#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4923#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4924#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4925#define EXP_WORD 0x80 /* expand word in parameter expansion */
4926#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4927/*
4928 * _rmescape() flags
4929 */
4930#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4931#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4932#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4933#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4934#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4935
4936/*
4937 * Structure specifying which parts of the string should be searched
4938 * for IFS characters.
4939 */
4940struct ifsregion {
4941 struct ifsregion *next; /* next region in list */
4942 int begoff; /* offset of start of region */
4943 int endoff; /* offset of end of region */
4944 int nulonly; /* search for nul bytes only */
4945};
4946
4947struct arglist {
4948 struct strlist *list;
4949 struct strlist **lastp;
4950};
4951
4952/* output of current string */
4953static char *expdest;
4954/* list of back quote expressions */
4955static struct nodelist *argbackq;
4956/* first struct in list of ifs regions */
4957static struct ifsregion ifsfirst;
4958/* last struct in list */
4959static struct ifsregion *ifslastp;
4960/* holds expanded arg list */
4961static struct arglist exparg;
4962
4963/*
4964 * Our own itoa().
4965 */
4966static int
4967cvtnum(arith_t num)
4968{
4969 int len;
4970
4971 expdest = makestrspace(32, expdest);
4972#if ENABLE_ASH_MATH_SUPPORT_64
4973 len = fmtstr(expdest, 32, "%lld", (long long) num);
4974#else
4975 len = fmtstr(expdest, 32, "%ld", num);
4976#endif
4977 STADJUST(len, expdest);
4978 return len;
4979}
4980
4981static size_t
4982esclen(const char *start, const char *p)
4983{
4984 size_t esc = 0;
4985
4986 while (p > start && *--p == CTLESC) {
4987 esc++;
4988 }
4989 return esc;
4990}
4991
4992/*
4993 * Remove any CTLESC characters from a string.
4994 */
4995static char *
4996_rmescapes(char *str, int flag)
4997{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004998 static const char qchars[] = { CTLESC, CTLQUOTEMARK, '\0' };
4999
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005000 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005001 unsigned inquotes;
5002 int notescaped;
5003 int globbing;
5004
5005 p = strpbrk(str, qchars);
5006 if (!p) {
5007 return str;
5008 }
5009 q = p;
5010 r = str;
5011 if (flag & RMESCAPE_ALLOC) {
5012 size_t len = p - str;
5013 size_t fulllen = len + strlen(p) + 1;
5014
5015 if (flag & RMESCAPE_GROW) {
5016 r = makestrspace(fulllen, expdest);
5017 } else if (flag & RMESCAPE_HEAP) {
5018 r = ckmalloc(fulllen);
5019 } else {
5020 r = stalloc(fulllen);
5021 }
5022 q = r;
5023 if (len > 0) {
5024 q = memcpy(q, str, len) + len;
5025 }
5026 }
5027 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5028 globbing = flag & RMESCAPE_GLOB;
5029 notescaped = globbing;
5030 while (*p) {
5031 if (*p == CTLQUOTEMARK) {
5032 inquotes = ~inquotes;
5033 p++;
5034 notescaped = globbing;
5035 continue;
5036 }
5037 if (*p == '\\') {
5038 /* naked back slash */
5039 notescaped = 0;
5040 goto copy;
5041 }
5042 if (*p == CTLESC) {
5043 p++;
5044 if (notescaped && inquotes && *p != '/') {
5045 *q++ = '\\';
5046 }
5047 }
5048 notescaped = globbing;
5049 copy:
5050 *q++ = *p++;
5051 }
5052 *q = '\0';
5053 if (flag & RMESCAPE_GROW) {
5054 expdest = r;
5055 STADJUST(q - r + 1, expdest);
5056 }
5057 return r;
5058}
5059#define rmescapes(p) _rmescapes((p), 0)
5060
5061#define pmatch(a, b) !fnmatch((a), (b), 0)
5062
5063/*
5064 * Prepare a pattern for a expmeta (internal glob(3)) call.
5065 *
5066 * Returns an stalloced string.
5067 */
5068static char *
5069preglob(const char *pattern, int quoted, int flag)
5070{
5071 flag |= RMESCAPE_GLOB;
5072 if (quoted) {
5073 flag |= RMESCAPE_QUOTED;
5074 }
5075 return _rmescapes((char *)pattern, flag);
5076}
5077
5078/*
5079 * Put a string on the stack.
5080 */
5081static void
5082memtodest(const char *p, size_t len, int syntax, int quotes)
5083{
5084 char *q = expdest;
5085
5086 q = makestrspace(len * 2, q);
5087
5088 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005089 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005090 if (!c)
5091 continue;
5092 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5093 USTPUTC(CTLESC, q);
5094 USTPUTC(c, q);
5095 }
5096
5097 expdest = q;
5098}
5099
5100static void
5101strtodest(const char *p, int syntax, int quotes)
5102{
5103 memtodest(p, strlen(p), syntax, quotes);
5104}
5105
5106/*
5107 * Record the fact that we have to scan this region of the
5108 * string for IFS characters.
5109 */
5110static void
5111recordregion(int start, int end, int nulonly)
5112{
5113 struct ifsregion *ifsp;
5114
5115 if (ifslastp == NULL) {
5116 ifsp = &ifsfirst;
5117 } else {
5118 INT_OFF;
5119 ifsp = ckmalloc(sizeof(*ifsp));
5120 ifsp->next = NULL;
5121 ifslastp->next = ifsp;
5122 INT_ON;
5123 }
5124 ifslastp = ifsp;
5125 ifslastp->begoff = start;
5126 ifslastp->endoff = end;
5127 ifslastp->nulonly = nulonly;
5128}
5129
5130static void
5131removerecordregions(int endoff)
5132{
5133 if (ifslastp == NULL)
5134 return;
5135
5136 if (ifsfirst.endoff > endoff) {
5137 while (ifsfirst.next != NULL) {
5138 struct ifsregion *ifsp;
5139 INT_OFF;
5140 ifsp = ifsfirst.next->next;
5141 free(ifsfirst.next);
5142 ifsfirst.next = ifsp;
5143 INT_ON;
5144 }
5145 if (ifsfirst.begoff > endoff)
5146 ifslastp = NULL;
5147 else {
5148 ifslastp = &ifsfirst;
5149 ifsfirst.endoff = endoff;
5150 }
5151 return;
5152 }
5153
5154 ifslastp = &ifsfirst;
5155 while (ifslastp->next && ifslastp->next->begoff < endoff)
5156 ifslastp=ifslastp->next;
5157 while (ifslastp->next != NULL) {
5158 struct ifsregion *ifsp;
5159 INT_OFF;
5160 ifsp = ifslastp->next->next;
5161 free(ifslastp->next);
5162 ifslastp->next = ifsp;
5163 INT_ON;
5164 }
5165 if (ifslastp->endoff > endoff)
5166 ifslastp->endoff = endoff;
5167}
5168
5169static char *
5170exptilde(char *startp, char *p, int flag)
5171{
5172 char c;
5173 char *name;
5174 struct passwd *pw;
5175 const char *home;
5176 int quotes = flag & (EXP_FULL | EXP_CASE);
5177 int startloc;
5178
5179 name = p + 1;
5180
5181 while ((c = *++p) != '\0') {
5182 switch (c) {
5183 case CTLESC:
5184 return startp;
5185 case CTLQUOTEMARK:
5186 return startp;
5187 case ':':
5188 if (flag & EXP_VARTILDE)
5189 goto done;
5190 break;
5191 case '/':
5192 case CTLENDVAR:
5193 goto done;
5194 }
5195 }
5196 done:
5197 *p = '\0';
5198 if (*name == '\0') {
5199 home = lookupvar(homestr);
5200 } else {
5201 pw = getpwnam(name);
5202 if (pw == NULL)
5203 goto lose;
5204 home = pw->pw_dir;
5205 }
5206 if (!home || !*home)
5207 goto lose;
5208 *p = c;
5209 startloc = expdest - (char *)stackblock();
5210 strtodest(home, SQSYNTAX, quotes);
5211 recordregion(startloc, expdest - (char *)stackblock(), 0);
5212 return p;
5213 lose:
5214 *p = c;
5215 return startp;
5216}
5217
5218/*
5219 * Execute a command inside back quotes. If it's a builtin command, we
5220 * want to save its output in a block obtained from malloc. Otherwise
5221 * we fork off a subprocess and get the output of the command via a pipe.
5222 * Should be called with interrupts off.
5223 */
5224struct backcmd { /* result of evalbackcmd */
5225 int fd; /* file descriptor to read from */
5226 char *buf; /* buffer */
5227 int nleft; /* number of chars in buffer */
5228 struct job *jp; /* job structure for command */
5229};
5230
5231/* These forward decls are needed to use "eval" code for backticks handling: */
5232static int back_exitstatus; /* exit status of backquoted command */
5233#define EV_EXIT 01 /* exit after evaluating tree */
5234static void evaltree(union node *, int);
5235
5236static void
5237evalbackcmd(union node *n, struct backcmd *result)
5238{
5239 int saveherefd;
5240
5241 result->fd = -1;
5242 result->buf = NULL;
5243 result->nleft = 0;
5244 result->jp = NULL;
5245 if (n == NULL) {
5246 goto out;
5247 }
5248
5249 saveherefd = herefd;
5250 herefd = -1;
5251
5252 {
5253 int pip[2];
5254 struct job *jp;
5255
5256 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005257 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005258 jp = makejob(n, 1);
5259 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5260 FORCE_INT_ON;
5261 close(pip[0]);
5262 if (pip[1] != 1) {
5263 close(1);
5264 copyfd(pip[1], 1);
5265 close(pip[1]);
5266 }
5267 eflag = 0;
5268 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5269 /* NOTREACHED */
5270 }
5271 close(pip[1]);
5272 result->fd = pip[0];
5273 result->jp = jp;
5274 }
5275 herefd = saveherefd;
5276 out:
5277 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5278 result->fd, result->buf, result->nleft, result->jp));
5279}
5280
5281/*
5282 * Expand stuff in backwards quotes.
5283 */
5284static void
5285expbackq(union node *cmd, int quoted, int quotes)
5286{
5287 struct backcmd in;
5288 int i;
5289 char buf[128];
5290 char *p;
5291 char *dest;
5292 int startloc;
5293 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5294 struct stackmark smark;
5295
5296 INT_OFF;
5297 setstackmark(&smark);
5298 dest = expdest;
5299 startloc = dest - (char *)stackblock();
5300 grabstackstr(dest);
5301 evalbackcmd(cmd, &in);
5302 popstackmark(&smark);
5303
5304 p = in.buf;
5305 i = in.nleft;
5306 if (i == 0)
5307 goto read;
5308 for (;;) {
5309 memtodest(p, i, syntax, quotes);
5310 read:
5311 if (in.fd < 0)
5312 break;
5313 i = safe_read(in.fd, buf, sizeof(buf));
5314 TRACE(("expbackq: read returns %d\n", i));
5315 if (i <= 0)
5316 break;
5317 p = buf;
5318 }
5319
5320 if (in.buf)
5321 free(in.buf);
5322 if (in.fd >= 0) {
5323 close(in.fd);
5324 back_exitstatus = waitforjob(in.jp);
5325 }
5326 INT_ON;
5327
5328 /* Eat all trailing newlines */
5329 dest = expdest;
5330 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5331 STUNPUTC(dest);
5332 expdest = dest;
5333
5334 if (quoted == 0)
5335 recordregion(startloc, dest - (char *)stackblock(), 0);
5336 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5337 (dest - (char *)stackblock()) - startloc,
5338 (dest - (char *)stackblock()) - startloc,
5339 stackblock() + startloc));
5340}
5341
5342#if ENABLE_ASH_MATH_SUPPORT
5343/*
5344 * Expand arithmetic expression. Backup to start of expression,
5345 * evaluate, place result in (backed up) result, adjust string position.
5346 */
5347static void
5348expari(int quotes)
5349{
5350 char *p, *start;
5351 int begoff;
5352 int flag;
5353 int len;
5354
5355 /* ifsfree(); */
5356
5357 /*
5358 * This routine is slightly over-complicated for
5359 * efficiency. Next we scan backwards looking for the
5360 * start of arithmetic.
5361 */
5362 start = stackblock();
5363 p = expdest - 1;
5364 *p = '\0';
5365 p--;
5366 do {
5367 int esc;
5368
5369 while (*p != CTLARI) {
5370 p--;
5371#if DEBUG
5372 if (p < start) {
5373 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5374 }
5375#endif
5376 }
5377
5378 esc = esclen(start, p);
5379 if (!(esc % 2)) {
5380 break;
5381 }
5382
5383 p -= esc + 1;
5384 } while (1);
5385
5386 begoff = p - start;
5387
5388 removerecordregions(begoff);
5389
5390 flag = p[1];
5391
5392 expdest = p;
5393
5394 if (quotes)
5395 rmescapes(p + 2);
5396
5397 len = cvtnum(dash_arith(p + 2));
5398
5399 if (flag != '"')
5400 recordregion(begoff, begoff + len, 0);
5401}
5402#endif
5403
5404/* argstr needs it */
5405static char *evalvar(char *p, int flag);
5406
5407/*
5408 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5409 * characters to allow for further processing. Otherwise treat
5410 * $@ like $* since no splitting will be performed.
5411 */
5412static void
5413argstr(char *p, int flag)
5414{
5415 static const char spclchars[] = {
5416 '=',
5417 ':',
5418 CTLQUOTEMARK,
5419 CTLENDVAR,
5420 CTLESC,
5421 CTLVAR,
5422 CTLBACKQ,
5423 CTLBACKQ | CTLQUOTE,
5424#if ENABLE_ASH_MATH_SUPPORT
5425 CTLENDARI,
5426#endif
5427 0
5428 };
5429 const char *reject = spclchars;
5430 int c;
5431 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5432 int breakall = flag & EXP_WORD;
5433 int inquotes;
5434 size_t length;
5435 int startloc;
5436
5437 if (!(flag & EXP_VARTILDE)) {
5438 reject += 2;
5439 } else if (flag & EXP_VARTILDE2) {
5440 reject++;
5441 }
5442 inquotes = 0;
5443 length = 0;
5444 if (flag & EXP_TILDE) {
5445 char *q;
5446
5447 flag &= ~EXP_TILDE;
5448 tilde:
5449 q = p;
5450 if (*q == CTLESC && (flag & EXP_QWORD))
5451 q++;
5452 if (*q == '~')
5453 p = exptilde(p, q, flag);
5454 }
5455 start:
5456 startloc = expdest - (char *)stackblock();
5457 for (;;) {
5458 length += strcspn(p + length, reject);
5459 c = p[length];
5460 if (c && (!(c & 0x80)
5461#if ENABLE_ASH_MATH_SUPPORT
5462 || c == CTLENDARI
5463#endif
5464 )) {
5465 /* c == '=' || c == ':' || c == CTLENDARI */
5466 length++;
5467 }
5468 if (length > 0) {
5469 int newloc;
5470 expdest = stack_nputstr(p, length, expdest);
5471 newloc = expdest - (char *)stackblock();
5472 if (breakall && !inquotes && newloc > startloc) {
5473 recordregion(startloc, newloc, 0);
5474 }
5475 startloc = newloc;
5476 }
5477 p += length + 1;
5478 length = 0;
5479
5480 switch (c) {
5481 case '\0':
5482 goto breakloop;
5483 case '=':
5484 if (flag & EXP_VARTILDE2) {
5485 p--;
5486 continue;
5487 }
5488 flag |= EXP_VARTILDE2;
5489 reject++;
5490 /* fall through */
5491 case ':':
5492 /*
5493 * sort of a hack - expand tildes in variable
5494 * assignments (after the first '=' and after ':'s).
5495 */
5496 if (*--p == '~') {
5497 goto tilde;
5498 }
5499 continue;
5500 }
5501
5502 switch (c) {
5503 case CTLENDVAR: /* ??? */
5504 goto breakloop;
5505 case CTLQUOTEMARK:
5506 /* "$@" syntax adherence hack */
5507 if (
5508 !inquotes &&
5509 !memcmp(p, dolatstr, 4) &&
5510 (p[4] == CTLQUOTEMARK || (
5511 p[4] == CTLENDVAR &&
5512 p[5] == CTLQUOTEMARK
5513 ))
5514 ) {
5515 p = evalvar(p + 1, flag) + 1;
5516 goto start;
5517 }
5518 inquotes = !inquotes;
5519 addquote:
5520 if (quotes) {
5521 p--;
5522 length++;
5523 startloc++;
5524 }
5525 break;
5526 case CTLESC:
5527 startloc++;
5528 length++;
5529 goto addquote;
5530 case CTLVAR:
5531 p = evalvar(p, flag);
5532 goto start;
5533 case CTLBACKQ:
5534 c = 0;
5535 case CTLBACKQ|CTLQUOTE:
5536 expbackq(argbackq->n, c, quotes);
5537 argbackq = argbackq->next;
5538 goto start;
5539#if ENABLE_ASH_MATH_SUPPORT
5540 case CTLENDARI:
5541 p--;
5542 expari(quotes);
5543 goto start;
5544#endif
5545 }
5546 }
5547 breakloop:
5548 ;
5549}
5550
5551static char *
5552scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5553 int zero)
5554{
5555 char *loc;
5556 char *loc2;
5557 char c;
5558
5559 loc = startp;
5560 loc2 = rmesc;
5561 do {
5562 int match;
5563 const char *s = loc2;
5564 c = *loc2;
5565 if (zero) {
5566 *loc2 = '\0';
5567 s = rmesc;
5568 }
5569 match = pmatch(str, s);
5570 *loc2 = c;
5571 if (match)
5572 return loc;
5573 if (quotes && *loc == CTLESC)
5574 loc++;
5575 loc++;
5576 loc2++;
5577 } while (c);
5578 return 0;
5579}
5580
5581static char *
5582scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5583 int zero)
5584{
5585 int esc = 0;
5586 char *loc;
5587 char *loc2;
5588
5589 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5590 int match;
5591 char c = *loc2;
5592 const char *s = loc2;
5593 if (zero) {
5594 *loc2 = '\0';
5595 s = rmesc;
5596 }
5597 match = pmatch(str, s);
5598 *loc2 = c;
5599 if (match)
5600 return loc;
5601 loc--;
5602 if (quotes) {
5603 if (--esc < 0) {
5604 esc = esclen(startp, loc);
5605 }
5606 if (esc % 2) {
5607 esc--;
5608 loc--;
5609 }
5610 }
5611 }
5612 return 0;
5613}
5614
5615static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5616static void
5617varunset(const char *end, const char *var, const char *umsg, int varflags)
5618{
5619 const char *msg;
5620 const char *tail;
5621
5622 tail = nullstr;
5623 msg = "parameter not set";
5624 if (umsg) {
5625 if (*end == CTLENDVAR) {
5626 if (varflags & VSNUL)
5627 tail = " or null";
5628 } else
5629 msg = umsg;
5630 }
5631 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5632}
5633
5634static const char *
5635subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5636{
5637 char *startp;
5638 char *loc;
5639 int saveherefd = herefd;
5640 struct nodelist *saveargbackq = argbackq;
5641 int amount;
5642 char *rmesc, *rmescend;
5643 int zero;
5644 char *(*scan)(char *, char *, char *, char *, int , int);
5645
5646 herefd = -1;
5647 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5648 STPUTC('\0', expdest);
5649 herefd = saveherefd;
5650 argbackq = saveargbackq;
5651 startp = stackblock() + startloc;
5652
5653 switch (subtype) {
5654 case VSASSIGN:
5655 setvar(str, startp, 0);
5656 amount = startp - expdest;
5657 STADJUST(amount, expdest);
5658 return startp;
5659
5660 case VSQUESTION:
5661 varunset(p, str, startp, varflags);
5662 /* NOTREACHED */
5663 }
5664
5665 subtype -= VSTRIMRIGHT;
5666#if DEBUG
5667 if (subtype < 0 || subtype > 3)
5668 abort();
5669#endif
5670
5671 rmesc = startp;
5672 rmescend = stackblock() + strloc;
5673 if (quotes) {
5674 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5675 if (rmesc != startp) {
5676 rmescend = expdest;
5677 startp = stackblock() + startloc;
5678 }
5679 }
5680 rmescend--;
5681 str = stackblock() + strloc;
5682 preglob(str, varflags & VSQUOTE, 0);
5683
5684 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5685 zero = subtype >> 1;
5686 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5687 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5688
5689 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5690 if (loc) {
5691 if (zero) {
5692 memmove(startp, loc, str - loc);
5693 loc = startp + (str - loc) - 1;
5694 }
5695 *loc = '\0';
5696 amount = loc - expdest;
5697 STADJUST(amount, expdest);
5698 }
5699 return loc;
5700}
5701
5702/*
5703 * Add the value of a specialized variable to the stack string.
5704 */
5705static ssize_t
5706varvalue(char *name, int varflags, int flags)
5707{
5708 int num;
5709 char *p;
5710 int i;
5711 int sep = 0;
5712 int sepq = 0;
5713 ssize_t len = 0;
5714 char **ap;
5715 int syntax;
5716 int quoted = varflags & VSQUOTE;
5717 int subtype = varflags & VSTYPE;
5718 int quotes = flags & (EXP_FULL | EXP_CASE);
5719
5720 if (quoted && (flags & EXP_FULL))
5721 sep = 1 << CHAR_BIT;
5722
5723 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5724 switch (*name) {
5725 case '$':
5726 num = rootpid;
5727 goto numvar;
5728 case '?':
5729 num = exitstatus;
5730 goto numvar;
5731 case '#':
5732 num = shellparam.nparam;
5733 goto numvar;
5734 case '!':
5735 num = backgndpid;
5736 if (num == 0)
5737 return -1;
5738 numvar:
5739 len = cvtnum(num);
5740 break;
5741 case '-':
5742 p = makestrspace(NOPTS, expdest);
5743 for (i = NOPTS - 1; i >= 0; i--) {
5744 if (optlist[i]) {
5745 USTPUTC(optletters(i), p);
5746 len++;
5747 }
5748 }
5749 expdest = p;
5750 break;
5751 case '@':
5752 if (sep)
5753 goto param;
5754 /* fall through */
5755 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005756 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005757 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5758 sepq = 1;
5759 param:
5760 ap = shellparam.p;
5761 if (!ap)
5762 return -1;
5763 while ((p = *ap++)) {
5764 size_t partlen;
5765
5766 partlen = strlen(p);
5767 len += partlen;
5768
5769 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5770 memtodest(p, partlen, syntax, quotes);
5771
5772 if (*ap && sep) {
5773 char *q;
5774
5775 len++;
5776 if (subtype == VSPLUS || subtype == VSLENGTH) {
5777 continue;
5778 }
5779 q = expdest;
5780 if (sepq)
5781 STPUTC(CTLESC, q);
5782 STPUTC(sep, q);
5783 expdest = q;
5784 }
5785 }
5786 return len;
5787 case '0':
5788 case '1':
5789 case '2':
5790 case '3':
5791 case '4':
5792 case '5':
5793 case '6':
5794 case '7':
5795 case '8':
5796 case '9':
5797 num = atoi(name);
5798 if (num < 0 || num > shellparam.nparam)
5799 return -1;
5800 p = num ? shellparam.p[num - 1] : arg0;
5801 goto value;
5802 default:
5803 p = lookupvar(name);
5804 value:
5805 if (!p)
5806 return -1;
5807
5808 len = strlen(p);
5809 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5810 memtodest(p, len, syntax, quotes);
5811 return len;
5812 }
5813
5814 if (subtype == VSPLUS || subtype == VSLENGTH)
5815 STADJUST(-len, expdest);
5816 return len;
5817}
5818
5819/*
5820 * Expand a variable, and return a pointer to the next character in the
5821 * input string.
5822 */
5823static char *
5824evalvar(char *p, int flag)
5825{
5826 int subtype;
5827 int varflags;
5828 char *var;
5829 int patloc;
5830 int c;
5831 int startloc;
5832 ssize_t varlen;
5833 int easy;
5834 int quotes;
5835 int quoted;
5836
5837 quotes = flag & (EXP_FULL | EXP_CASE);
5838 varflags = *p++;
5839 subtype = varflags & VSTYPE;
5840 quoted = varflags & VSQUOTE;
5841 var = p;
5842 easy = (!quoted || (*var == '@' && shellparam.nparam));
5843 startloc = expdest - (char *)stackblock();
5844 p = strchr(p, '=') + 1;
5845
5846 again:
5847 varlen = varvalue(var, varflags, flag);
5848 if (varflags & VSNUL)
5849 varlen--;
5850
5851 if (subtype == VSPLUS) {
5852 varlen = -1 - varlen;
5853 goto vsplus;
5854 }
5855
5856 if (subtype == VSMINUS) {
5857 vsplus:
5858 if (varlen < 0) {
5859 argstr(
5860 p, flag | EXP_TILDE |
5861 (quoted ? EXP_QWORD : EXP_WORD)
5862 );
5863 goto end;
5864 }
5865 if (easy)
5866 goto record;
5867 goto end;
5868 }
5869
5870 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5871 if (varlen < 0) {
5872 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5873 varflags &= ~VSNUL;
5874 /*
5875 * Remove any recorded regions beyond
5876 * start of variable
5877 */
5878 removerecordregions(startloc);
5879 goto again;
5880 }
5881 goto end;
5882 }
5883 if (easy)
5884 goto record;
5885 goto end;
5886 }
5887
5888 if (varlen < 0 && uflag)
5889 varunset(p, var, 0, 0);
5890
5891 if (subtype == VSLENGTH) {
5892 cvtnum(varlen > 0 ? varlen : 0);
5893 goto record;
5894 }
5895
5896 if (subtype == VSNORMAL) {
5897 if (!easy)
5898 goto end;
5899 record:
5900 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5901 goto end;
5902 }
5903
5904#if DEBUG
5905 switch (subtype) {
5906 case VSTRIMLEFT:
5907 case VSTRIMLEFTMAX:
5908 case VSTRIMRIGHT:
5909 case VSTRIMRIGHTMAX:
5910 break;
5911 default:
5912 abort();
5913 }
5914#endif
5915
5916 if (varlen >= 0) {
5917 /*
5918 * Terminate the string and start recording the pattern
5919 * right after it
5920 */
5921 STPUTC('\0', expdest);
5922 patloc = expdest - (char *)stackblock();
5923 if (subevalvar(p, NULL, patloc, subtype,
5924 startloc, varflags, quotes) == 0) {
5925 int amount = expdest - (
5926 (char *)stackblock() + patloc - 1
5927 );
5928 STADJUST(-amount, expdest);
5929 }
5930 /* Remove any recorded regions beyond start of variable */
5931 removerecordregions(startloc);
5932 goto record;
5933 }
5934
5935 end:
5936 if (subtype != VSNORMAL) { /* skip to end of alternative */
5937 int nesting = 1;
5938 for (;;) {
5939 c = *p++;
5940 if (c == CTLESC)
5941 p++;
5942 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5943 if (varlen >= 0)
5944 argbackq = argbackq->next;
5945 } else if (c == CTLVAR) {
5946 if ((*p++ & VSTYPE) != VSNORMAL)
5947 nesting++;
5948 } else if (c == CTLENDVAR) {
5949 if (--nesting == 0)
5950 break;
5951 }
5952 }
5953 }
5954 return p;
5955}
5956
5957/*
5958 * Break the argument string into pieces based upon IFS and add the
5959 * strings to the argument list. The regions of the string to be
5960 * searched for IFS characters have been stored by recordregion.
5961 */
5962static void
5963ifsbreakup(char *string, struct arglist *arglist)
5964{
5965 struct ifsregion *ifsp;
5966 struct strlist *sp;
5967 char *start;
5968 char *p;
5969 char *q;
5970 const char *ifs, *realifs;
5971 int ifsspc;
5972 int nulonly;
5973
5974 start = string;
5975 if (ifslastp != NULL) {
5976 ifsspc = 0;
5977 nulonly = 0;
5978 realifs = ifsset() ? ifsval() : defifs;
5979 ifsp = &ifsfirst;
5980 do {
5981 p = string + ifsp->begoff;
5982 nulonly = ifsp->nulonly;
5983 ifs = nulonly ? nullstr : realifs;
5984 ifsspc = 0;
5985 while (p < string + ifsp->endoff) {
5986 q = p;
5987 if (*p == CTLESC)
5988 p++;
5989 if (!strchr(ifs, *p)) {
5990 p++;
5991 continue;
5992 }
5993 if (!nulonly)
5994 ifsspc = (strchr(defifs, *p) != NULL);
5995 /* Ignore IFS whitespace at start */
5996 if (q == start && ifsspc) {
5997 p++;
5998 start = p;
5999 continue;
6000 }
6001 *q = '\0';
6002 sp = stalloc(sizeof(*sp));
6003 sp->text = start;
6004 *arglist->lastp = sp;
6005 arglist->lastp = &sp->next;
6006 p++;
6007 if (!nulonly) {
6008 for (;;) {
6009 if (p >= string + ifsp->endoff) {
6010 break;
6011 }
6012 q = p;
6013 if (*p == CTLESC)
6014 p++;
6015 if (strchr(ifs, *p) == NULL ) {
6016 p = q;
6017 break;
6018 } else if (strchr(defifs, *p) == NULL) {
6019 if (ifsspc) {
6020 p++;
6021 ifsspc = 0;
6022 } else {
6023 p = q;
6024 break;
6025 }
6026 } else
6027 p++;
6028 }
6029 }
6030 start = p;
6031 } /* while */
6032 ifsp = ifsp->next;
6033 } while (ifsp != NULL);
6034 if (nulonly)
6035 goto add;
6036 }
6037
6038 if (!*start)
6039 return;
6040
6041 add:
6042 sp = stalloc(sizeof(*sp));
6043 sp->text = start;
6044 *arglist->lastp = sp;
6045 arglist->lastp = &sp->next;
6046}
6047
6048static void
6049ifsfree(void)
6050{
6051 struct ifsregion *p;
6052
6053 INT_OFF;
6054 p = ifsfirst.next;
6055 do {
6056 struct ifsregion *ifsp;
6057 ifsp = p->next;
6058 free(p);
6059 p = ifsp;
6060 } while (p);
6061 ifslastp = NULL;
6062 ifsfirst.next = NULL;
6063 INT_ON;
6064}
6065
6066/*
6067 * Add a file name to the list.
6068 */
6069static void
6070addfname(const char *name)
6071{
6072 struct strlist *sp;
6073
6074 sp = stalloc(sizeof(*sp));
6075 sp->text = ststrdup(name);
6076 *exparg.lastp = sp;
6077 exparg.lastp = &sp->next;
6078}
6079
6080static char *expdir;
6081
6082/*
6083 * Do metacharacter (i.e. *, ?, [...]) expansion.
6084 */
6085static void
6086expmeta(char *enddir, char *name)
6087{
6088 char *p;
6089 const char *cp;
6090 char *start;
6091 char *endname;
6092 int metaflag;
6093 struct stat statb;
6094 DIR *dirp;
6095 struct dirent *dp;
6096 int atend;
6097 int matchdot;
6098
6099 metaflag = 0;
6100 start = name;
6101 for (p = name; *p; p++) {
6102 if (*p == '*' || *p == '?')
6103 metaflag = 1;
6104 else if (*p == '[') {
6105 char *q = p + 1;
6106 if (*q == '!')
6107 q++;
6108 for (;;) {
6109 if (*q == '\\')
6110 q++;
6111 if (*q == '/' || *q == '\0')
6112 break;
6113 if (*++q == ']') {
6114 metaflag = 1;
6115 break;
6116 }
6117 }
6118 } else if (*p == '\\')
6119 p++;
6120 else if (*p == '/') {
6121 if (metaflag)
6122 goto out;
6123 start = p + 1;
6124 }
6125 }
6126 out:
6127 if (metaflag == 0) { /* we've reached the end of the file name */
6128 if (enddir != expdir)
6129 metaflag++;
6130 p = name;
6131 do {
6132 if (*p == '\\')
6133 p++;
6134 *enddir++ = *p;
6135 } while (*p++);
6136 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6137 addfname(expdir);
6138 return;
6139 }
6140 endname = p;
6141 if (name < start) {
6142 p = name;
6143 do {
6144 if (*p == '\\')
6145 p++;
6146 *enddir++ = *p++;
6147 } while (p < start);
6148 }
6149 if (enddir == expdir) {
6150 cp = ".";
6151 } else if (enddir == expdir + 1 && *expdir == '/') {
6152 cp = "/";
6153 } else {
6154 cp = expdir;
6155 enddir[-1] = '\0';
6156 }
6157 dirp = opendir(cp);
6158 if (dirp == NULL)
6159 return;
6160 if (enddir != expdir)
6161 enddir[-1] = '/';
6162 if (*endname == 0) {
6163 atend = 1;
6164 } else {
6165 atend = 0;
6166 *endname++ = '\0';
6167 }
6168 matchdot = 0;
6169 p = start;
6170 if (*p == '\\')
6171 p++;
6172 if (*p == '.')
6173 matchdot++;
6174 while (! intpending && (dp = readdir(dirp)) != NULL) {
6175 if (dp->d_name[0] == '.' && ! matchdot)
6176 continue;
6177 if (pmatch(start, dp->d_name)) {
6178 if (atend) {
6179 strcpy(enddir, dp->d_name);
6180 addfname(expdir);
6181 } else {
6182 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6183 continue;
6184 p[-1] = '/';
6185 expmeta(p, endname);
6186 }
6187 }
6188 }
6189 closedir(dirp);
6190 if (! atend)
6191 endname[-1] = '/';
6192}
6193
6194static struct strlist *
6195msort(struct strlist *list, int len)
6196{
6197 struct strlist *p, *q = NULL;
6198 struct strlist **lpp;
6199 int half;
6200 int n;
6201
6202 if (len <= 1)
6203 return list;
6204 half = len >> 1;
6205 p = list;
6206 for (n = half; --n >= 0; ) {
6207 q = p;
6208 p = p->next;
6209 }
6210 q->next = NULL; /* terminate first half of list */
6211 q = msort(list, half); /* sort first half of list */
6212 p = msort(p, len - half); /* sort second half */
6213 lpp = &list;
6214 for (;;) {
6215#if ENABLE_LOCALE_SUPPORT
6216 if (strcoll(p->text, q->text) < 0)
6217#else
6218 if (strcmp(p->text, q->text) < 0)
6219#endif
6220 {
6221 *lpp = p;
6222 lpp = &p->next;
6223 p = *lpp;
6224 if (p == NULL) {
6225 *lpp = q;
6226 break;
6227 }
6228 } else {
6229 *lpp = q;
6230 lpp = &q->next;
6231 q = *lpp;
6232 if (q == NULL) {
6233 *lpp = p;
6234 break;
6235 }
6236 }
6237 }
6238 return list;
6239}
6240
6241/*
6242 * Sort the results of file name expansion. It calculates the number of
6243 * strings to sort and then calls msort (short for merge sort) to do the
6244 * work.
6245 */
6246static struct strlist *
6247expsort(struct strlist *str)
6248{
6249 int len;
6250 struct strlist *sp;
6251
6252 len = 0;
6253 for (sp = str; sp; sp = sp->next)
6254 len++;
6255 return msort(str, len);
6256}
6257
6258static void
6259expandmeta(struct strlist *str, int flag)
6260{
6261 static const char metachars[] = {
6262 '*', '?', '[', 0
6263 };
6264 /* TODO - EXP_REDIR */
6265
6266 while (str) {
6267 struct strlist **savelastp;
6268 struct strlist *sp;
6269 char *p;
6270
6271 if (fflag)
6272 goto nometa;
6273 if (!strpbrk(str->text, metachars))
6274 goto nometa;
6275 savelastp = exparg.lastp;
6276
6277 INT_OFF;
6278 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6279 {
6280 int i = strlen(str->text);
6281 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6282 }
6283
6284 expmeta(expdir, p);
6285 free(expdir);
6286 if (p != str->text)
6287 free(p);
6288 INT_ON;
6289 if (exparg.lastp == savelastp) {
6290 /*
6291 * no matches
6292 */
6293 nometa:
6294 *exparg.lastp = str;
6295 rmescapes(str->text);
6296 exparg.lastp = &str->next;
6297 } else {
6298 *exparg.lastp = NULL;
6299 *savelastp = sp = expsort(*savelastp);
6300 while (sp->next != NULL)
6301 sp = sp->next;
6302 exparg.lastp = &sp->next;
6303 }
6304 str = str->next;
6305 }
6306}
6307
6308/*
6309 * Perform variable substitution and command substitution on an argument,
6310 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6311 * perform splitting and file name expansion. When arglist is NULL, perform
6312 * here document expansion.
6313 */
6314static void
6315expandarg(union node *arg, struct arglist *arglist, int flag)
6316{
6317 struct strlist *sp;
6318 char *p;
6319
6320 argbackq = arg->narg.backquote;
6321 STARTSTACKSTR(expdest);
6322 ifsfirst.next = NULL;
6323 ifslastp = NULL;
6324 argstr(arg->narg.text, flag);
6325 p = _STPUTC('\0', expdest);
6326 expdest = p - 1;
6327 if (arglist == NULL) {
6328 return; /* here document expanded */
6329 }
6330 p = grabstackstr(p);
6331 exparg.lastp = &exparg.list;
6332 /*
6333 * TODO - EXP_REDIR
6334 */
6335 if (flag & EXP_FULL) {
6336 ifsbreakup(p, &exparg);
6337 *exparg.lastp = NULL;
6338 exparg.lastp = &exparg.list;
6339 expandmeta(exparg.list, flag);
6340 } else {
6341 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6342 rmescapes(p);
6343 sp = stalloc(sizeof(*sp));
6344 sp->text = p;
6345 *exparg.lastp = sp;
6346 exparg.lastp = &sp->next;
6347 }
6348 if (ifsfirst.next)
6349 ifsfree();
6350 *exparg.lastp = NULL;
6351 if (exparg.list) {
6352 *arglist->lastp = exparg.list;
6353 arglist->lastp = exparg.lastp;
6354 }
6355}
6356
6357/*
6358 * Expand shell variables and backquotes inside a here document.
6359 */
6360static void
6361expandhere(union node *arg, int fd)
6362{
6363 herefd = fd;
6364 expandarg(arg, (struct arglist *)NULL, 0);
6365 full_write(fd, stackblock(), expdest - (char *)stackblock());
6366}
6367
6368/*
6369 * Returns true if the pattern matches the string.
6370 */
6371static int
6372patmatch(char *pattern, const char *string)
6373{
6374 return pmatch(preglob(pattern, 0, 0), string);
6375}
6376
6377/*
6378 * See if a pattern matches in a case statement.
6379 */
6380static int
6381casematch(union node *pattern, char *val)
6382{
6383 struct stackmark smark;
6384 int result;
6385
6386 setstackmark(&smark);
6387 argbackq = pattern->narg.backquote;
6388 STARTSTACKSTR(expdest);
6389 ifslastp = NULL;
6390 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6391 STACKSTRNUL(expdest);
6392 result = patmatch(stackblock(), val);
6393 popstackmark(&smark);
6394 return result;
6395}
6396
6397
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006398/* ============ find_command */
6399
6400struct builtincmd {
6401 const char *name;
6402 int (*builtin)(int, char **);
6403 /* unsigned flags; */
6404};
6405#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6406#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6407#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6408
6409struct cmdentry {
6410 int cmdtype;
6411 union param {
6412 int index;
6413 const struct builtincmd *cmd;
6414 struct funcnode *func;
6415 } u;
6416};
6417/* values of cmdtype */
6418#define CMDUNKNOWN -1 /* no entry in table for command */
6419#define CMDNORMAL 0 /* command is an executable program */
6420#define CMDFUNCTION 1 /* command is a shell function */
6421#define CMDBUILTIN 2 /* command is a shell builtin */
6422
6423/* action to find_command() */
6424#define DO_ERR 0x01 /* prints errors */
6425#define DO_ABS 0x02 /* checks absolute paths */
6426#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6427#define DO_ALTPATH 0x08 /* using alternate path */
6428#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6429
6430static void find_command(char *, struct cmdentry *, int, const char *);
6431
6432
6433/* ============ Hashing commands */
6434
6435/*
6436 * When commands are first encountered, they are entered in a hash table.
6437 * This ensures that a full path search will not have to be done for them
6438 * on each invocation.
6439 *
6440 * We should investigate converting to a linear search, even though that
6441 * would make the command name "hash" a misnomer.
6442 */
6443
6444#define CMDTABLESIZE 31 /* should be prime */
6445#define ARB 1 /* actual size determined at run time */
6446
6447struct tblentry {
6448 struct tblentry *next; /* next entry in hash chain */
6449 union param param; /* definition of builtin function */
6450 short cmdtype; /* index identifying command */
6451 char rehash; /* if set, cd done since entry created */
6452 char cmdname[ARB]; /* name of command */
6453};
6454
6455static struct tblentry *cmdtable[CMDTABLESIZE];
6456static int builtinloc = -1; /* index in path of %builtin, or -1 */
6457
6458static void
6459tryexec(char *cmd, char **argv, char **envp)
6460{
6461 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006462
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006463#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006464 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006465 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006466
6467 a = find_applet_by_name(cmd);
6468 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006469 if (a->noexec) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006470 current_applet = a;
Denis Vlasenkof5294e12007-04-14 10:09:57 +00006471 run_current_applet_and_exit(argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006472 }
6473 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006474 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006475 /* If they called chroot or otherwise made the binary no longer
6476 * executable, fall through */
6477 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006478 }
6479#endif
6480
6481 repeat:
6482#ifdef SYSV
6483 do {
6484 execve(cmd, argv, envp);
6485 } while (errno == EINTR);
6486#else
6487 execve(cmd, argv, envp);
6488#endif
6489 if (repeated++) {
6490 free(argv);
6491 } else if (errno == ENOEXEC) {
6492 char **ap;
6493 char **new;
6494
6495 for (ap = argv; *ap; ap++)
6496 ;
6497 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6498 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006499 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006500 ap += 2;
6501 argv++;
6502 while ((*ap++ = *argv++))
6503 ;
6504 argv = new;
6505 goto repeat;
6506 }
6507}
6508
6509/*
6510 * Exec a program. Never returns. If you change this routine, you may
6511 * have to change the find_command routine as well.
6512 */
6513#define environment() listvars(VEXPORT, VUNSET, 0)
6514static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6515static void
6516shellexec(char **argv, const char *path, int idx)
6517{
6518 char *cmdname;
6519 int e;
6520 char **envp;
6521 int exerrno;
6522
6523 clearredir(1);
6524 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006525 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006526#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006527 || find_applet_by_name(argv[0])
6528#endif
6529 ) {
6530 tryexec(argv[0], argv, envp);
6531 e = errno;
6532 } else {
6533 e = ENOENT;
6534 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6535 if (--idx < 0 && pathopt == NULL) {
6536 tryexec(cmdname, argv, envp);
6537 if (errno != ENOENT && errno != ENOTDIR)
6538 e = errno;
6539 }
6540 stunalloc(cmdname);
6541 }
6542 }
6543
6544 /* Map to POSIX errors */
6545 switch (e) {
6546 case EACCES:
6547 exerrno = 126;
6548 break;
6549 case ENOENT:
6550 exerrno = 127;
6551 break;
6552 default:
6553 exerrno = 2;
6554 break;
6555 }
6556 exitstatus = exerrno;
6557 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6558 argv[0], e, suppressint ));
6559 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6560 /* NOTREACHED */
6561}
6562
6563static void
6564printentry(struct tblentry *cmdp)
6565{
6566 int idx;
6567 const char *path;
6568 char *name;
6569
6570 idx = cmdp->param.index;
6571 path = pathval();
6572 do {
6573 name = padvance(&path, cmdp->cmdname);
6574 stunalloc(name);
6575 } while (--idx >= 0);
6576 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6577}
6578
6579/*
6580 * Clear out command entries. The argument specifies the first entry in
6581 * PATH which has changed.
6582 */
6583static void
6584clearcmdentry(int firstchange)
6585{
6586 struct tblentry **tblp;
6587 struct tblentry **pp;
6588 struct tblentry *cmdp;
6589
6590 INT_OFF;
6591 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6592 pp = tblp;
6593 while ((cmdp = *pp) != NULL) {
6594 if ((cmdp->cmdtype == CMDNORMAL &&
6595 cmdp->param.index >= firstchange)
6596 || (cmdp->cmdtype == CMDBUILTIN &&
6597 builtinloc >= firstchange)
6598 ) {
6599 *pp = cmdp->next;
6600 free(cmdp);
6601 } else {
6602 pp = &cmdp->next;
6603 }
6604 }
6605 }
6606 INT_ON;
6607}
6608
6609/*
6610 * Locate a command in the command hash table. If "add" is nonzero,
6611 * add the command to the table if it is not already present. The
6612 * variable "lastcmdentry" is set to point to the address of the link
6613 * pointing to the entry, so that delete_cmd_entry can delete the
6614 * entry.
6615 *
6616 * Interrupts must be off if called with add != 0.
6617 */
6618static struct tblentry **lastcmdentry;
6619
6620static struct tblentry *
6621cmdlookup(const char *name, int add)
6622{
6623 unsigned int hashval;
6624 const char *p;
6625 struct tblentry *cmdp;
6626 struct tblentry **pp;
6627
6628 p = name;
6629 hashval = (unsigned char)*p << 4;
6630 while (*p)
6631 hashval += (unsigned char)*p++;
6632 hashval &= 0x7FFF;
6633 pp = &cmdtable[hashval % CMDTABLESIZE];
6634 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6635 if (strcmp(cmdp->cmdname, name) == 0)
6636 break;
6637 pp = &cmdp->next;
6638 }
6639 if (add && cmdp == NULL) {
6640 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6641 + strlen(name) + 1);
6642 cmdp->next = NULL;
6643 cmdp->cmdtype = CMDUNKNOWN;
6644 strcpy(cmdp->cmdname, name);
6645 }
6646 lastcmdentry = pp;
6647 return cmdp;
6648}
6649
6650/*
6651 * Delete the command entry returned on the last lookup.
6652 */
6653static void
6654delete_cmd_entry(void)
6655{
6656 struct tblentry *cmdp;
6657
6658 INT_OFF;
6659 cmdp = *lastcmdentry;
6660 *lastcmdentry = cmdp->next;
6661 if (cmdp->cmdtype == CMDFUNCTION)
6662 freefunc(cmdp->param.func);
6663 free(cmdp);
6664 INT_ON;
6665}
6666
6667/*
6668 * Add a new command entry, replacing any existing command entry for
6669 * the same name - except special builtins.
6670 */
6671static void
6672addcmdentry(char *name, struct cmdentry *entry)
6673{
6674 struct tblentry *cmdp;
6675
6676 cmdp = cmdlookup(name, 1);
6677 if (cmdp->cmdtype == CMDFUNCTION) {
6678 freefunc(cmdp->param.func);
6679 }
6680 cmdp->cmdtype = entry->cmdtype;
6681 cmdp->param = entry->u;
6682 cmdp->rehash = 0;
6683}
6684
6685static int
6686hashcmd(int argc, char **argv)
6687{
6688 struct tblentry **pp;
6689 struct tblentry *cmdp;
6690 int c;
6691 struct cmdentry entry;
6692 char *name;
6693
6694 while ((c = nextopt("r")) != '\0') {
6695 clearcmdentry(0);
6696 return 0;
6697 }
6698 if (*argptr == NULL) {
6699 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6700 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6701 if (cmdp->cmdtype == CMDNORMAL)
6702 printentry(cmdp);
6703 }
6704 }
6705 return 0;
6706 }
6707 c = 0;
6708 while ((name = *argptr) != NULL) {
6709 cmdp = cmdlookup(name, 0);
6710 if (cmdp != NULL
6711 && (cmdp->cmdtype == CMDNORMAL
6712 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6713 delete_cmd_entry();
6714 find_command(name, &entry, DO_ERR, pathval());
6715 if (entry.cmdtype == CMDUNKNOWN)
6716 c = 1;
6717 argptr++;
6718 }
6719 return c;
6720}
6721
6722/*
6723 * Called when a cd is done. Marks all commands so the next time they
6724 * are executed they will be rehashed.
6725 */
6726static void
6727hashcd(void)
6728{
6729 struct tblentry **pp;
6730 struct tblentry *cmdp;
6731
6732 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6733 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6734 if (cmdp->cmdtype == CMDNORMAL || (
6735 cmdp->cmdtype == CMDBUILTIN &&
6736 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6737 builtinloc > 0
6738 ))
6739 cmdp->rehash = 1;
6740 }
6741 }
6742}
6743
6744/*
6745 * Fix command hash table when PATH changed.
6746 * Called before PATH is changed. The argument is the new value of PATH;
6747 * pathval() still returns the old value at this point.
6748 * Called with interrupts off.
6749 */
6750static void
6751changepath(const char *newval)
6752{
6753 const char *old, *new;
6754 int idx;
6755 int firstchange;
6756 int idx_bltin;
6757
6758 old = pathval();
6759 new = newval;
6760 firstchange = 9999; /* assume no change */
6761 idx = 0;
6762 idx_bltin = -1;
6763 for (;;) {
6764 if (*old != *new) {
6765 firstchange = idx;
6766 if ((*old == '\0' && *new == ':')
6767 || (*old == ':' && *new == '\0'))
6768 firstchange++;
6769 old = new; /* ignore subsequent differences */
6770 }
6771 if (*new == '\0')
6772 break;
6773 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6774 idx_bltin = idx;
6775 if (*new == ':') {
6776 idx++;
6777 }
6778 new++, old++;
6779 }
6780 if (builtinloc < 0 && idx_bltin >= 0)
6781 builtinloc = idx_bltin; /* zap builtins */
6782 if (builtinloc >= 0 && idx_bltin < 0)
6783 firstchange = 0;
6784 clearcmdentry(firstchange);
6785 builtinloc = idx_bltin;
6786}
6787
6788#define TEOF 0
6789#define TNL 1
6790#define TREDIR 2
6791#define TWORD 3
6792#define TSEMI 4
6793#define TBACKGND 5
6794#define TAND 6
6795#define TOR 7
6796#define TPIPE 8
6797#define TLP 9
6798#define TRP 10
6799#define TENDCASE 11
6800#define TENDBQUOTE 12
6801#define TNOT 13
6802#define TCASE 14
6803#define TDO 15
6804#define TDONE 16
6805#define TELIF 17
6806#define TELSE 18
6807#define TESAC 19
6808#define TFI 20
6809#define TFOR 21
6810#define TIF 22
6811#define TIN 23
6812#define TTHEN 24
6813#define TUNTIL 25
6814#define TWHILE 26
6815#define TBEGIN 27
6816#define TEND 28
6817
6818/* first char is indicating which tokens mark the end of a list */
6819static const char *const tokname_array[] = {
6820 "\1end of file",
6821 "\0newline",
6822 "\0redirection",
6823 "\0word",
6824 "\0;",
6825 "\0&",
6826 "\0&&",
6827 "\0||",
6828 "\0|",
6829 "\0(",
6830 "\1)",
6831 "\1;;",
6832 "\1`",
6833#define KWDOFFSET 13
6834 /* the following are keywords */
6835 "\0!",
6836 "\0case",
6837 "\1do",
6838 "\1done",
6839 "\1elif",
6840 "\1else",
6841 "\1esac",
6842 "\1fi",
6843 "\0for",
6844 "\0if",
6845 "\0in",
6846 "\1then",
6847 "\0until",
6848 "\0while",
6849 "\0{",
6850 "\1}",
6851};
6852
6853static const char *
6854tokname(int tok)
6855{
6856 static char buf[16];
6857
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006858//try this:
6859//if (tok < TSEMI) return tokname_array[tok] + 1;
6860//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6861//return buf;
6862
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006863 if (tok >= TSEMI)
6864 buf[0] = '"';
6865 sprintf(buf + (tok >= TSEMI), "%s%c",
6866 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6867 return buf;
6868}
6869
6870/* Wrapper around strcmp for qsort/bsearch/... */
6871static int
6872pstrcmp(const void *a, const void *b)
6873{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006874 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006875}
6876
6877static const char *const *
6878findkwd(const char *s)
6879{
6880 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006881 (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
6882 sizeof(char *), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006883}
6884
6885/*
6886 * Locate and print what a word is...
6887 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006888static int
6889describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006890{
6891 struct cmdentry entry;
6892 struct tblentry *cmdp;
6893#if ENABLE_ASH_ALIAS
6894 const struct alias *ap;
6895#endif
6896 const char *path = pathval();
6897
6898 if (describe_command_verbose) {
6899 out1str(command);
6900 }
6901
6902 /* First look at the keywords */
6903 if (findkwd(command)) {
6904 out1str(describe_command_verbose ? " is a shell keyword" : command);
6905 goto out;
6906 }
6907
6908#if ENABLE_ASH_ALIAS
6909 /* Then look at the aliases */
6910 ap = lookupalias(command, 0);
6911 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00006912 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006913 out1str("alias ");
6914 printalias(ap);
6915 return 0;
6916 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00006917 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006918 goto out;
6919 }
6920#endif
6921 /* Then check if it is a tracked alias */
6922 cmdp = cmdlookup(command, 0);
6923 if (cmdp != NULL) {
6924 entry.cmdtype = cmdp->cmdtype;
6925 entry.u = cmdp->param;
6926 } else {
6927 /* Finally use brute force */
6928 find_command(command, &entry, DO_ABS, path);
6929 }
6930
6931 switch (entry.cmdtype) {
6932 case CMDNORMAL: {
6933 int j = entry.u.index;
6934 char *p;
6935 if (j == -1) {
6936 p = command;
6937 } else {
6938 do {
6939 p = padvance(&path, command);
6940 stunalloc(p);
6941 } while (--j >= 0);
6942 }
6943 if (describe_command_verbose) {
6944 out1fmt(" is%s %s",
6945 (cmdp ? " a tracked alias for" : nullstr), p
6946 );
6947 } else {
6948 out1str(p);
6949 }
6950 break;
6951 }
6952
6953 case CMDFUNCTION:
6954 if (describe_command_verbose) {
6955 out1str(" is a shell function");
6956 } else {
6957 out1str(command);
6958 }
6959 break;
6960
6961 case CMDBUILTIN:
6962 if (describe_command_verbose) {
6963 out1fmt(" is a %sshell builtin",
6964 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
6965 "special " : nullstr
6966 );
6967 } else {
6968 out1str(command);
6969 }
6970 break;
6971
6972 default:
6973 if (describe_command_verbose) {
6974 out1str(": not found\n");
6975 }
6976 return 127;
6977 }
6978 out:
6979 outstr("\n", stdout);
6980 return 0;
6981}
6982
6983static int
6984typecmd(int argc, char **argv)
6985{
Denis Vlasenko46846e22007-05-20 13:08:31 +00006986 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006987 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00006988 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006989
Denis Vlasenko46846e22007-05-20 13:08:31 +00006990 /* type -p ... ? (we don't bother checking for 'p') */
6991 if (argv[1][0] == '-') {
6992 i++;
6993 verbose = 0;
6994 }
6995 while (i < argc) {
6996 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006997 }
6998 return err;
6999}
7000
7001#if ENABLE_ASH_CMDCMD
7002static int
7003commandcmd(int argc, char **argv)
7004{
7005 int c;
7006 enum {
7007 VERIFY_BRIEF = 1,
7008 VERIFY_VERBOSE = 2,
7009 } verify = 0;
7010
7011 while ((c = nextopt("pvV")) != '\0')
7012 if (c == 'V')
7013 verify |= VERIFY_VERBOSE;
7014 else if (c == 'v')
7015 verify |= VERIFY_BRIEF;
7016#if DEBUG
7017 else if (c != 'p')
7018 abort();
7019#endif
7020 if (verify)
7021 return describe_command(*argptr, verify - VERIFY_BRIEF);
7022
7023 return 0;
7024}
7025#endif
7026
7027
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007028/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007029
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007030static int funcblocksize; /* size of structures in function */
7031static int funcstringsize; /* size of strings in node */
7032static void *funcblock; /* block to allocate function from */
7033static char *funcstring; /* block to allocate strings from */
7034
Eric Andersencb57d552001-06-28 07:25:16 +00007035/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007036#define EV_EXIT 01 /* exit after evaluating tree */
7037#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7038#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007039
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007040static const short nodesize[26] = {
7041 SHELL_ALIGN(sizeof(struct ncmd)),
7042 SHELL_ALIGN(sizeof(struct npipe)),
7043 SHELL_ALIGN(sizeof(struct nredir)),
7044 SHELL_ALIGN(sizeof(struct nredir)),
7045 SHELL_ALIGN(sizeof(struct nredir)),
7046 SHELL_ALIGN(sizeof(struct nbinary)),
7047 SHELL_ALIGN(sizeof(struct nbinary)),
7048 SHELL_ALIGN(sizeof(struct nbinary)),
7049 SHELL_ALIGN(sizeof(struct nif)),
7050 SHELL_ALIGN(sizeof(struct nbinary)),
7051 SHELL_ALIGN(sizeof(struct nbinary)),
7052 SHELL_ALIGN(sizeof(struct nfor)),
7053 SHELL_ALIGN(sizeof(struct ncase)),
7054 SHELL_ALIGN(sizeof(struct nclist)),
7055 SHELL_ALIGN(sizeof(struct narg)),
7056 SHELL_ALIGN(sizeof(struct narg)),
7057 SHELL_ALIGN(sizeof(struct nfile)),
7058 SHELL_ALIGN(sizeof(struct nfile)),
7059 SHELL_ALIGN(sizeof(struct nfile)),
7060 SHELL_ALIGN(sizeof(struct nfile)),
7061 SHELL_ALIGN(sizeof(struct nfile)),
7062 SHELL_ALIGN(sizeof(struct ndup)),
7063 SHELL_ALIGN(sizeof(struct ndup)),
7064 SHELL_ALIGN(sizeof(struct nhere)),
7065 SHELL_ALIGN(sizeof(struct nhere)),
7066 SHELL_ALIGN(sizeof(struct nnot)),
7067};
7068
7069static void calcsize(union node *n);
7070
7071static void
7072sizenodelist(struct nodelist *lp)
7073{
7074 while (lp) {
7075 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7076 calcsize(lp->n);
7077 lp = lp->next;
7078 }
7079}
7080
7081static void
7082calcsize(union node *n)
7083{
7084 if (n == NULL)
7085 return;
7086 funcblocksize += nodesize[n->type];
7087 switch (n->type) {
7088 case NCMD:
7089 calcsize(n->ncmd.redirect);
7090 calcsize(n->ncmd.args);
7091 calcsize(n->ncmd.assign);
7092 break;
7093 case NPIPE:
7094 sizenodelist(n->npipe.cmdlist);
7095 break;
7096 case NREDIR:
7097 case NBACKGND:
7098 case NSUBSHELL:
7099 calcsize(n->nredir.redirect);
7100 calcsize(n->nredir.n);
7101 break;
7102 case NAND:
7103 case NOR:
7104 case NSEMI:
7105 case NWHILE:
7106 case NUNTIL:
7107 calcsize(n->nbinary.ch2);
7108 calcsize(n->nbinary.ch1);
7109 break;
7110 case NIF:
7111 calcsize(n->nif.elsepart);
7112 calcsize(n->nif.ifpart);
7113 calcsize(n->nif.test);
7114 break;
7115 case NFOR:
7116 funcstringsize += strlen(n->nfor.var) + 1;
7117 calcsize(n->nfor.body);
7118 calcsize(n->nfor.args);
7119 break;
7120 case NCASE:
7121 calcsize(n->ncase.cases);
7122 calcsize(n->ncase.expr);
7123 break;
7124 case NCLIST:
7125 calcsize(n->nclist.body);
7126 calcsize(n->nclist.pattern);
7127 calcsize(n->nclist.next);
7128 break;
7129 case NDEFUN:
7130 case NARG:
7131 sizenodelist(n->narg.backquote);
7132 funcstringsize += strlen(n->narg.text) + 1;
7133 calcsize(n->narg.next);
7134 break;
7135 case NTO:
7136 case NCLOBBER:
7137 case NFROM:
7138 case NFROMTO:
7139 case NAPPEND:
7140 calcsize(n->nfile.fname);
7141 calcsize(n->nfile.next);
7142 break;
7143 case NTOFD:
7144 case NFROMFD:
7145 calcsize(n->ndup.vname);
7146 calcsize(n->ndup.next);
7147 break;
7148 case NHERE:
7149 case NXHERE:
7150 calcsize(n->nhere.doc);
7151 calcsize(n->nhere.next);
7152 break;
7153 case NNOT:
7154 calcsize(n->nnot.com);
7155 break;
7156 };
7157}
7158
7159static char *
7160nodeckstrdup(char *s)
7161{
7162 char *rtn = funcstring;
7163
7164 strcpy(funcstring, s);
7165 funcstring += strlen(s) + 1;
7166 return rtn;
7167}
7168
7169static union node *copynode(union node *);
7170
7171static struct nodelist *
7172copynodelist(struct nodelist *lp)
7173{
7174 struct nodelist *start;
7175 struct nodelist **lpp;
7176
7177 lpp = &start;
7178 while (lp) {
7179 *lpp = funcblock;
7180 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7181 (*lpp)->n = copynode(lp->n);
7182 lp = lp->next;
7183 lpp = &(*lpp)->next;
7184 }
7185 *lpp = NULL;
7186 return start;
7187}
7188
7189static union node *
7190copynode(union node *n)
7191{
7192 union node *new;
7193
7194 if (n == NULL)
7195 return NULL;
7196 new = funcblock;
7197 funcblock = (char *) funcblock + nodesize[n->type];
7198
7199 switch (n->type) {
7200 case NCMD:
7201 new->ncmd.redirect = copynode(n->ncmd.redirect);
7202 new->ncmd.args = copynode(n->ncmd.args);
7203 new->ncmd.assign = copynode(n->ncmd.assign);
7204 break;
7205 case NPIPE:
7206 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7207 new->npipe.backgnd = n->npipe.backgnd;
7208 break;
7209 case NREDIR:
7210 case NBACKGND:
7211 case NSUBSHELL:
7212 new->nredir.redirect = copynode(n->nredir.redirect);
7213 new->nredir.n = copynode(n->nredir.n);
7214 break;
7215 case NAND:
7216 case NOR:
7217 case NSEMI:
7218 case NWHILE:
7219 case NUNTIL:
7220 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7221 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7222 break;
7223 case NIF:
7224 new->nif.elsepart = copynode(n->nif.elsepart);
7225 new->nif.ifpart = copynode(n->nif.ifpart);
7226 new->nif.test = copynode(n->nif.test);
7227 break;
7228 case NFOR:
7229 new->nfor.var = nodeckstrdup(n->nfor.var);
7230 new->nfor.body = copynode(n->nfor.body);
7231 new->nfor.args = copynode(n->nfor.args);
7232 break;
7233 case NCASE:
7234 new->ncase.cases = copynode(n->ncase.cases);
7235 new->ncase.expr = copynode(n->ncase.expr);
7236 break;
7237 case NCLIST:
7238 new->nclist.body = copynode(n->nclist.body);
7239 new->nclist.pattern = copynode(n->nclist.pattern);
7240 new->nclist.next = copynode(n->nclist.next);
7241 break;
7242 case NDEFUN:
7243 case NARG:
7244 new->narg.backquote = copynodelist(n->narg.backquote);
7245 new->narg.text = nodeckstrdup(n->narg.text);
7246 new->narg.next = copynode(n->narg.next);
7247 break;
7248 case NTO:
7249 case NCLOBBER:
7250 case NFROM:
7251 case NFROMTO:
7252 case NAPPEND:
7253 new->nfile.fname = copynode(n->nfile.fname);
7254 new->nfile.fd = n->nfile.fd;
7255 new->nfile.next = copynode(n->nfile.next);
7256 break;
7257 case NTOFD:
7258 case NFROMFD:
7259 new->ndup.vname = copynode(n->ndup.vname);
7260 new->ndup.dupfd = n->ndup.dupfd;
7261 new->ndup.fd = n->ndup.fd;
7262 new->ndup.next = copynode(n->ndup.next);
7263 break;
7264 case NHERE:
7265 case NXHERE:
7266 new->nhere.doc = copynode(n->nhere.doc);
7267 new->nhere.fd = n->nhere.fd;
7268 new->nhere.next = copynode(n->nhere.next);
7269 break;
7270 case NNOT:
7271 new->nnot.com = copynode(n->nnot.com);
7272 break;
7273 };
7274 new->type = n->type;
7275 return new;
7276}
7277
7278/*
7279 * Make a copy of a parse tree.
7280 */
7281static struct funcnode *
7282copyfunc(union node *n)
7283{
7284 struct funcnode *f;
7285 size_t blocksize;
7286
7287 funcblocksize = offsetof(struct funcnode, n);
7288 funcstringsize = 0;
7289 calcsize(n);
7290 blocksize = funcblocksize;
7291 f = ckmalloc(blocksize + funcstringsize);
7292 funcblock = (char *) f + offsetof(struct funcnode, n);
7293 funcstring = (char *) f + blocksize;
7294 copynode(n);
7295 f->count = 0;
7296 return f;
7297}
7298
7299/*
7300 * Define a shell function.
7301 */
7302static void
7303defun(char *name, union node *func)
7304{
7305 struct cmdentry entry;
7306
7307 INT_OFF;
7308 entry.cmdtype = CMDFUNCTION;
7309 entry.u.func = copyfunc(func);
7310 addcmdentry(name, &entry);
7311 INT_ON;
7312}
7313
7314static int evalskip; /* set if we are skipping commands */
7315/* reasons for skipping commands (see comment on breakcmd routine) */
7316#define SKIPBREAK (1 << 0)
7317#define SKIPCONT (1 << 1)
7318#define SKIPFUNC (1 << 2)
7319#define SKIPFILE (1 << 3)
7320#define SKIPEVAL (1 << 4)
7321static int skipcount; /* number of levels to skip */
7322static int funcnest; /* depth of function calls */
7323
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007324/* forward decl way out to parsing code - dotrap needs it */
7325static int evalstring(char *s, int mask);
7326
7327/*
7328 * Called to execute a trap. Perhaps we should avoid entering new trap
7329 * handlers while we are executing a trap handler.
7330 */
7331static int
7332dotrap(void)
7333{
7334 char *p;
7335 char *q;
7336 int i;
7337 int savestatus;
7338 int skip = 0;
7339
7340 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007341 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007342 xbarrier();
7343
7344 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7345 if (!*q)
7346 continue;
7347 *q = '\0';
7348
7349 p = trap[i + 1];
7350 if (!p)
7351 continue;
7352 skip = evalstring(p, SKIPEVAL);
7353 exitstatus = savestatus;
7354 if (skip)
7355 break;
7356 }
7357
7358 return skip;
7359}
7360
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007361/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007362static void evalloop(union node *, int);
7363static void evalfor(union node *, int);
7364static void evalcase(union node *, int);
7365static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007366static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007367static void evalpipe(union node *, int);
7368static void evalcommand(union node *, int);
7369static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007370static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007371
Eric Andersen62483552001-07-10 06:09:16 +00007372/*
Eric Andersenc470f442003-07-28 09:56:35 +00007373 * Evaluate a parse tree. The value is left in the global variable
7374 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007375 */
Eric Andersenc470f442003-07-28 09:56:35 +00007376static void
7377evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007378{
Eric Andersenc470f442003-07-28 09:56:35 +00007379 int checkexit = 0;
7380 void (*evalfn)(union node *, int);
7381 unsigned isor;
7382 int status;
7383 if (n == NULL) {
7384 TRACE(("evaltree(NULL) called\n"));
7385 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007386 }
Eric Andersenc470f442003-07-28 09:56:35 +00007387 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007388 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007389 switch (n->type) {
7390 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007391#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007392 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007393 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007394 break;
7395#endif
7396 case NNOT:
7397 evaltree(n->nnot.com, EV_TESTED);
7398 status = !exitstatus;
7399 goto setstatus;
7400 case NREDIR:
7401 expredir(n->nredir.redirect);
7402 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7403 if (!status) {
7404 evaltree(n->nredir.n, flags & EV_TESTED);
7405 status = exitstatus;
7406 }
7407 popredir(0);
7408 goto setstatus;
7409 case NCMD:
7410 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007411 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007412 if (eflag && !(flags & EV_TESTED))
7413 checkexit = ~0;
7414 goto calleval;
7415 case NFOR:
7416 evalfn = evalfor;
7417 goto calleval;
7418 case NWHILE:
7419 case NUNTIL:
7420 evalfn = evalloop;
7421 goto calleval;
7422 case NSUBSHELL:
7423 case NBACKGND:
7424 evalfn = evalsubshell;
7425 goto calleval;
7426 case NPIPE:
7427 evalfn = evalpipe;
7428 goto checkexit;
7429 case NCASE:
7430 evalfn = evalcase;
7431 goto calleval;
7432 case NAND:
7433 case NOR:
7434 case NSEMI:
7435#if NAND + 1 != NOR
7436#error NAND + 1 != NOR
7437#endif
7438#if NOR + 1 != NSEMI
7439#error NOR + 1 != NSEMI
7440#endif
7441 isor = n->type - NAND;
7442 evaltree(
7443 n->nbinary.ch1,
7444 (flags | ((isor >> 1) - 1)) & EV_TESTED
7445 );
7446 if (!exitstatus == isor)
7447 break;
7448 if (!evalskip) {
7449 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007450 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007451 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007452 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007453 evalfn(n, flags);
7454 break;
7455 }
7456 break;
7457 case NIF:
7458 evaltree(n->nif.test, EV_TESTED);
7459 if (evalskip)
7460 break;
7461 if (exitstatus == 0) {
7462 n = n->nif.ifpart;
7463 goto evaln;
7464 } else if (n->nif.elsepart) {
7465 n = n->nif.elsepart;
7466 goto evaln;
7467 }
7468 goto success;
7469 case NDEFUN:
7470 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007471 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007472 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007473 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007474 exitstatus = status;
7475 break;
7476 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007477 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007478 if ((checkexit & exitstatus))
7479 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007480 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007481 goto exexit;
7482
7483 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007484 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007485 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007486 }
Eric Andersen62483552001-07-10 06:09:16 +00007487}
7488
Eric Andersenc470f442003-07-28 09:56:35 +00007489#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7490static
7491#endif
7492void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7493
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007494static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007495
7496static void
7497evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007498{
7499 int status;
7500
7501 loopnest++;
7502 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007503 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007504 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007505 int i;
7506
Eric Andersencb57d552001-06-28 07:25:16 +00007507 evaltree(n->nbinary.ch1, EV_TESTED);
7508 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007509 skipping:
7510 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007511 evalskip = 0;
7512 continue;
7513 }
7514 if (evalskip == SKIPBREAK && --skipcount <= 0)
7515 evalskip = 0;
7516 break;
7517 }
Eric Andersenc470f442003-07-28 09:56:35 +00007518 i = exitstatus;
7519 if (n->type != NWHILE)
7520 i = !i;
7521 if (i != 0)
7522 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007523 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007524 status = exitstatus;
7525 if (evalskip)
7526 goto skipping;
7527 }
7528 loopnest--;
7529 exitstatus = status;
7530}
7531
Eric Andersenc470f442003-07-28 09:56:35 +00007532static void
7533evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007534{
7535 struct arglist arglist;
7536 union node *argp;
7537 struct strlist *sp;
7538 struct stackmark smark;
7539
7540 setstackmark(&smark);
7541 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007542 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007543 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007544 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007545 if (evalskip)
7546 goto out;
7547 }
7548 *arglist.lastp = NULL;
7549
7550 exitstatus = 0;
7551 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007552 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007553 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007554 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007555 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007556 if (evalskip) {
7557 if (evalskip == SKIPCONT && --skipcount <= 0) {
7558 evalskip = 0;
7559 continue;
7560 }
7561 if (evalskip == SKIPBREAK && --skipcount <= 0)
7562 evalskip = 0;
7563 break;
7564 }
7565 }
7566 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007567 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007568 popstackmark(&smark);
7569}
7570
Eric Andersenc470f442003-07-28 09:56:35 +00007571static void
7572evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007573{
7574 union node *cp;
7575 union node *patp;
7576 struct arglist arglist;
7577 struct stackmark smark;
7578
7579 setstackmark(&smark);
7580 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007581 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007582 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007583 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7584 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007585 if (casematch(patp, arglist.list->text)) {
7586 if (evalskip == 0) {
7587 evaltree(cp->nclist.body, flags);
7588 }
7589 goto out;
7590 }
7591 }
7592 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007593 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007594 popstackmark(&smark);
7595}
7596
Eric Andersenc470f442003-07-28 09:56:35 +00007597/*
7598 * Kick off a subshell to evaluate a tree.
7599 */
Eric Andersenc470f442003-07-28 09:56:35 +00007600static void
7601evalsubshell(union node *n, int flags)
7602{
7603 struct job *jp;
7604 int backgnd = (n->type == NBACKGND);
7605 int status;
7606
7607 expredir(n->nredir.redirect);
7608 if (!backgnd && flags & EV_EXIT && !trap[0])
7609 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007610 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007611 jp = makejob(n, 1);
7612 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007613 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007614 flags |= EV_EXIT;
7615 if (backgnd)
7616 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007617 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007618 redirect(n->nredir.redirect, 0);
7619 evaltreenr(n->nredir.n, flags);
7620 /* never returns */
7621 }
7622 status = 0;
7623 if (! backgnd)
7624 status = waitforjob(jp);
7625 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007626 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007627}
7628
Eric Andersenc470f442003-07-28 09:56:35 +00007629/*
7630 * Compute the names of the files in a redirection list.
7631 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007632static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007633static void
7634expredir(union node *n)
7635{
7636 union node *redir;
7637
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007638 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007639 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007640
7641 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007642 fn.lastp = &fn.list;
7643 switch (redir->type) {
7644 case NFROMTO:
7645 case NFROM:
7646 case NTO:
7647 case NCLOBBER:
7648 case NAPPEND:
7649 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7650 redir->nfile.expfname = fn.list->text;
7651 break;
7652 case NFROMFD:
7653 case NTOFD:
7654 if (redir->ndup.vname) {
7655 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007656 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007657 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007658 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007659 }
7660 break;
7661 }
7662 }
7663}
7664
Eric Andersencb57d552001-06-28 07:25:16 +00007665/*
Eric Andersencb57d552001-06-28 07:25:16 +00007666 * Evaluate a pipeline. All the processes in the pipeline are children
7667 * of the process creating the pipeline. (This differs from some versions
7668 * of the shell, which make the last process in a pipeline the parent
7669 * of all the rest.)
7670 */
Eric Andersenc470f442003-07-28 09:56:35 +00007671static void
7672evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007673{
7674 struct job *jp;
7675 struct nodelist *lp;
7676 int pipelen;
7677 int prevfd;
7678 int pip[2];
7679
Eric Andersenc470f442003-07-28 09:56:35 +00007680 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007681 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007682 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007683 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007684 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007685 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007686 jp = makejob(n, pipelen);
7687 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007688 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007689 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007690 pip[1] = -1;
7691 if (lp->next) {
7692 if (pipe(pip) < 0) {
7693 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007694 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007695 }
7696 }
7697 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007698 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007699 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007700 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007701 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007702 if (prevfd > 0) {
7703 dup2(prevfd, 0);
7704 close(prevfd);
7705 }
7706 if (pip[1] > 1) {
7707 dup2(pip[1], 1);
7708 close(pip[1]);
7709 }
Eric Andersenc470f442003-07-28 09:56:35 +00007710 evaltreenr(lp->n, flags);
7711 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007712 }
7713 if (prevfd >= 0)
7714 close(prevfd);
7715 prevfd = pip[0];
7716 close(pip[1]);
7717 }
Eric Andersencb57d552001-06-28 07:25:16 +00007718 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007719 exitstatus = waitforjob(jp);
7720 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007721 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007722 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007723}
7724
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007725/*
7726 * Controls whether the shell is interactive or not.
7727 */
7728static void
7729setinteractive(int on)
7730{
7731 static int is_interactive;
7732
7733 if (++on == is_interactive)
7734 return;
7735 is_interactive = on;
7736 setsignal(SIGINT);
7737 setsignal(SIGQUIT);
7738 setsignal(SIGTERM);
7739#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7740 if (is_interactive > 1) {
7741 /* Looks like they want an interactive shell */
7742 static smallint do_banner;
7743
7744 if (!do_banner) {
7745 out1fmt(
7746 "\n\n"
7747 "%s Built-in shell (ash)\n"
7748 "Enter 'help' for a list of built-in commands."
7749 "\n\n",
7750 BB_BANNER);
7751 do_banner = 1;
7752 }
7753 }
7754#endif
7755}
7756
7757#if ENABLE_FEATURE_EDITING_VI
7758#define setvimode(on) do { \
7759 if (on) line_input_state->flags |= VI_MODE; \
7760 else line_input_state->flags &= ~VI_MODE; \
7761} while (0)
7762#else
7763#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7764#endif
7765
7766static void
7767optschanged(void)
7768{
7769#if DEBUG
7770 opentrace();
7771#endif
7772 setinteractive(iflag);
7773 setjobctl(mflag);
7774 setvimode(viflag);
7775}
7776
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007777static struct localvar *localvars;
7778
7779/*
7780 * Called after a function returns.
7781 * Interrupts must be off.
7782 */
7783static void
7784poplocalvars(void)
7785{
7786 struct localvar *lvp;
7787 struct var *vp;
7788
7789 while ((lvp = localvars) != NULL) {
7790 localvars = lvp->next;
7791 vp = lvp->vp;
7792 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7793 if (vp == NULL) { /* $- saved */
7794 memcpy(optlist, lvp->text, sizeof(optlist));
7795 free((char*)lvp->text);
7796 optschanged();
7797 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7798 unsetvar(vp->text);
7799 } else {
7800 if (vp->func)
7801 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7802 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7803 free((char*)vp->text);
7804 vp->flags = lvp->flags;
7805 vp->text = lvp->text;
7806 }
7807 free(lvp);
7808 }
7809}
7810
7811static int
7812evalfun(struct funcnode *func, int argc, char **argv, int flags)
7813{
7814 volatile struct shparam saveparam;
7815 struct localvar *volatile savelocalvars;
7816 struct jmploc *volatile savehandler;
7817 struct jmploc jmploc;
7818 int e;
7819
7820 saveparam = shellparam;
7821 savelocalvars = localvars;
7822 e = setjmp(jmploc.loc);
7823 if (e) {
7824 goto funcdone;
7825 }
7826 INT_OFF;
7827 savehandler = exception_handler;
7828 exception_handler = &jmploc;
7829 localvars = NULL;
7830 shellparam.malloc = 0;
7831 func->count++;
7832 funcnest++;
7833 INT_ON;
7834 shellparam.nparam = argc - 1;
7835 shellparam.p = argv + 1;
7836#if ENABLE_ASH_GETOPTS
7837 shellparam.optind = 1;
7838 shellparam.optoff = -1;
7839#endif
7840 evaltree(&func->n, flags & EV_TESTED);
7841funcdone:
7842 INT_OFF;
7843 funcnest--;
7844 freefunc(func);
7845 poplocalvars();
7846 localvars = savelocalvars;
7847 freeparam(&shellparam);
7848 shellparam = saveparam;
7849 exception_handler = savehandler;
7850 INT_ON;
7851 evalskip &= ~SKIPFUNC;
7852 return e;
7853}
7854
Denis Vlasenko131ae172007-02-18 13:00:19 +00007855#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007856static char **
7857parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007858{
7859 char *cp, c;
7860
7861 for (;;) {
7862 cp = *++argv;
7863 if (!cp)
7864 return 0;
7865 if (*cp++ != '-')
7866 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007867 c = *cp++;
7868 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007869 break;
7870 if (c == '-' && !*cp) {
7871 argv++;
7872 break;
7873 }
7874 do {
7875 switch (c) {
7876 case 'p':
7877 *path = defpath;
7878 break;
7879 default:
7880 /* run 'typecmd' for other options */
7881 return 0;
7882 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007883 c = *cp++;
7884 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007885 }
7886 return argv;
7887}
7888#endif
7889
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007890/*
7891 * Make a variable a local variable. When a variable is made local, it's
7892 * value and flags are saved in a localvar structure. The saved values
7893 * will be restored when the shell function returns. We handle the name
7894 * "-" as a special case.
7895 */
7896static void
7897mklocal(char *name)
7898{
7899 struct localvar *lvp;
7900 struct var **vpp;
7901 struct var *vp;
7902
7903 INT_OFF;
7904 lvp = ckmalloc(sizeof(struct localvar));
7905 if (LONE_DASH(name)) {
7906 char *p;
7907 p = ckmalloc(sizeof(optlist));
7908 lvp->text = memcpy(p, optlist, sizeof(optlist));
7909 vp = NULL;
7910 } else {
7911 char *eq;
7912
7913 vpp = hashvar(name);
7914 vp = *findvar(vpp, name);
7915 eq = strchr(name, '=');
7916 if (vp == NULL) {
7917 if (eq)
7918 setvareq(name, VSTRFIXED);
7919 else
7920 setvar(name, NULL, VSTRFIXED);
7921 vp = *vpp; /* the new variable */
7922 lvp->flags = VUNSET;
7923 } else {
7924 lvp->text = vp->text;
7925 lvp->flags = vp->flags;
7926 vp->flags |= VSTRFIXED|VTEXTFIXED;
7927 if (eq)
7928 setvareq(name, 0);
7929 }
7930 }
7931 lvp->vp = vp;
7932 lvp->next = localvars;
7933 localvars = lvp;
7934 INT_ON;
7935}
7936
7937/*
7938 * The "local" command.
7939 */
7940static int
7941localcmd(int argc, char **argv)
7942{
7943 char *name;
7944
7945 argv = argptr;
7946 while ((name = *argv++) != NULL) {
7947 mklocal(name);
7948 }
7949 return 0;
7950}
7951
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00007952static int
7953falsecmd(int argc, char **argv)
7954{
7955 return 1;
7956}
7957
7958static int
7959truecmd(int argc, char **argv)
7960{
7961 return 0;
7962}
7963
7964static int
7965execcmd(int argc, char **argv)
7966{
7967 if (argc > 1) {
7968 iflag = 0; /* exit on error */
7969 mflag = 0;
7970 optschanged();
7971 shellexec(argv + 1, pathval(), 0);
7972 }
7973 return 0;
7974}
7975
7976/*
7977 * The return command.
7978 */
7979static int
7980returncmd(int argc, char **argv)
7981{
7982 /*
7983 * If called outside a function, do what ksh does;
7984 * skip the rest of the file.
7985 */
7986 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
7987 return argv[1] ? number(argv[1]) : exitstatus;
7988}
7989
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007990/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007991static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007992static int dotcmd(int, char **);
7993static int evalcmd(int, char **);
7994#if ENABLE_ASH_BUILTIN_ECHO
7995static int echocmd(int, char **);
7996#endif
7997#if ENABLE_ASH_BUILTIN_TEST
7998static int testcmd(int, char **);
7999#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008000static int exitcmd(int, char **);
8001static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008002#if ENABLE_ASH_GETOPTS
8003static int getoptscmd(int, char **);
8004#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008005#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8006static int helpcmd(int argc, char **argv);
8007#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008008#if ENABLE_ASH_MATH_SUPPORT
8009static int letcmd(int, char **);
8010#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008011static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008012static int setcmd(int, char **);
8013static int shiftcmd(int, char **);
8014static int timescmd(int, char **);
8015static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008016static int umaskcmd(int, char **);
8017static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008018static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008019
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008020#define BUILTIN_NOSPEC "0"
8021#define BUILTIN_SPECIAL "1"
8022#define BUILTIN_REGULAR "2"
8023#define BUILTIN_SPEC_REG "3"
8024#define BUILTIN_ASSIGN "4"
8025#define BUILTIN_SPEC_ASSG "5"
8026#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008027#define BUILTIN_SPEC_REG_ASSG "7"
8028
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008029/* make sure to keep these in proper order since it is searched via bsearch() */
8030static const struct builtincmd builtintab[] = {
8031 { BUILTIN_SPEC_REG ".", dotcmd },
8032 { BUILTIN_SPEC_REG ":", truecmd },
8033#if ENABLE_ASH_BUILTIN_TEST
8034 { BUILTIN_REGULAR "[", testcmd },
8035 { BUILTIN_REGULAR "[[", testcmd },
8036#endif
8037#if ENABLE_ASH_ALIAS
8038 { BUILTIN_REG_ASSG "alias", aliascmd },
8039#endif
8040#if JOBS
8041 { BUILTIN_REGULAR "bg", fg_bgcmd },
8042#endif
8043 { BUILTIN_SPEC_REG "break", breakcmd },
8044 { BUILTIN_REGULAR "cd", cdcmd },
8045 { BUILTIN_NOSPEC "chdir", cdcmd },
8046#if ENABLE_ASH_CMDCMD
8047 { BUILTIN_REGULAR "command", commandcmd },
8048#endif
8049 { BUILTIN_SPEC_REG "continue", breakcmd },
8050#if ENABLE_ASH_BUILTIN_ECHO
8051 { BUILTIN_REGULAR "echo", echocmd },
8052#endif
8053 { BUILTIN_SPEC_REG "eval", evalcmd },
8054 { BUILTIN_SPEC_REG "exec", execcmd },
8055 { BUILTIN_SPEC_REG "exit", exitcmd },
8056 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8057 { BUILTIN_REGULAR "false", falsecmd },
8058#if JOBS
8059 { BUILTIN_REGULAR "fg", fg_bgcmd },
8060#endif
8061#if ENABLE_ASH_GETOPTS
8062 { BUILTIN_REGULAR "getopts", getoptscmd },
8063#endif
8064 { BUILTIN_NOSPEC "hash", hashcmd },
8065#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8066 { BUILTIN_NOSPEC "help", helpcmd },
8067#endif
8068#if JOBS
8069 { BUILTIN_REGULAR "jobs", jobscmd },
8070 { BUILTIN_REGULAR "kill", killcmd },
8071#endif
8072#if ENABLE_ASH_MATH_SUPPORT
8073 { BUILTIN_NOSPEC "let", letcmd },
8074#endif
8075 { BUILTIN_ASSIGN "local", localcmd },
8076 { BUILTIN_NOSPEC "pwd", pwdcmd },
8077 { BUILTIN_REGULAR "read", readcmd },
8078 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8079 { BUILTIN_SPEC_REG "return", returncmd },
8080 { BUILTIN_SPEC_REG "set", setcmd },
8081 { BUILTIN_SPEC_REG "shift", shiftcmd },
8082 { BUILTIN_SPEC_REG "source", dotcmd },
8083#if ENABLE_ASH_BUILTIN_TEST
8084 { BUILTIN_REGULAR "test", testcmd },
8085#endif
8086 { BUILTIN_SPEC_REG "times", timescmd },
8087 { BUILTIN_SPEC_REG "trap", trapcmd },
8088 { BUILTIN_REGULAR "true", truecmd },
8089 { BUILTIN_NOSPEC "type", typecmd },
8090 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8091 { BUILTIN_REGULAR "umask", umaskcmd },
8092#if ENABLE_ASH_ALIAS
8093 { BUILTIN_REGULAR "unalias", unaliascmd },
8094#endif
8095 { BUILTIN_SPEC_REG "unset", unsetcmd },
8096 { BUILTIN_REGULAR "wait", waitcmd },
8097};
8098
8099#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
8100
8101#define COMMANDCMD (builtintab + 5 + \
8102 2 * ENABLE_ASH_BUILTIN_TEST + \
8103 ENABLE_ASH_ALIAS + \
8104 ENABLE_ASH_JOB_CONTROL)
8105#define EXECCMD (builtintab + 7 + \
8106 2 * ENABLE_ASH_BUILTIN_TEST + \
8107 ENABLE_ASH_ALIAS + \
8108 ENABLE_ASH_JOB_CONTROL + \
8109 ENABLE_ASH_CMDCMD + \
8110 ENABLE_ASH_BUILTIN_ECHO)
8111
8112/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008113 * Search the table of builtin commands.
8114 */
8115static struct builtincmd *
8116find_builtin(const char *name)
8117{
8118 struct builtincmd *bp;
8119
8120 bp = bsearch(
8121 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
8122 pstrcmp
8123 );
8124 return bp;
8125}
8126
8127/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008128 * Execute a simple command.
8129 */
8130static int back_exitstatus; /* exit status of backquoted command */
8131static int
8132isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008133{
8134 const char *q = endofname(p);
8135 if (p == q)
8136 return 0;
8137 return *q == '=';
8138}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008139static int
8140bltincmd(int argc, char **argv)
8141{
8142 /* Preserve exitstatus of a previous possible redirection
8143 * as POSIX mandates */
8144 return back_exitstatus;
8145}
Eric Andersenc470f442003-07-28 09:56:35 +00008146static void
8147evalcommand(union node *cmd, int flags)
8148{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008149 static const struct builtincmd bltin = {
8150 "\0\0", bltincmd
8151 };
Eric Andersenc470f442003-07-28 09:56:35 +00008152 struct stackmark smark;
8153 union node *argp;
8154 struct arglist arglist;
8155 struct arglist varlist;
8156 char **argv;
8157 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008158 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008159 struct cmdentry cmdentry;
8160 struct job *jp;
8161 char *lastarg;
8162 const char *path;
8163 int spclbltin;
8164 int cmd_is_exec;
8165 int status;
8166 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008167 struct builtincmd *bcmd;
8168 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008169
8170 /* First expand the arguments. */
8171 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8172 setstackmark(&smark);
8173 back_exitstatus = 0;
8174
8175 cmdentry.cmdtype = CMDBUILTIN;
8176 cmdentry.u.cmd = &bltin;
8177 varlist.lastp = &varlist.list;
8178 *varlist.lastp = NULL;
8179 arglist.lastp = &arglist.list;
8180 *arglist.lastp = NULL;
8181
8182 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008183 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008184 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8185 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8186 }
8187
Eric Andersenc470f442003-07-28 09:56:35 +00008188 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8189 struct strlist **spp;
8190
8191 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008192 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008193 expandarg(argp, &arglist, EXP_VARTILDE);
8194 else
8195 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8196
Eric Andersenc470f442003-07-28 09:56:35 +00008197 for (sp = *spp; sp; sp = sp->next)
8198 argc++;
8199 }
8200
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008201 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008202 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008203 TRACE(("evalcommand arg: %s\n", sp->text));
8204 *nargv++ = sp->text;
8205 }
8206 *nargv = NULL;
8207
8208 lastarg = NULL;
8209 if (iflag && funcnest == 0 && argc > 0)
8210 lastarg = nargv[-1];
8211
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008212 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008213 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008214 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008215
8216 path = vpath.text;
8217 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8218 struct strlist **spp;
8219 char *p;
8220
8221 spp = varlist.lastp;
8222 expandarg(argp, &varlist, EXP_VARTILDE);
8223
8224 /*
8225 * Modify the command lookup path, if a PATH= assignment
8226 * is present
8227 */
8228 p = (*spp)->text;
8229 if (varequal(p, path))
8230 path = p;
8231 }
8232
8233 /* Print the command if xflag is set. */
8234 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008235 int n;
8236 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008237
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008238 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008239 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008240
8241 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008242 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008243 while (sp) {
8244 dprintf(preverrout_fd, p, sp->text);
8245 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008246 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008247 p--;
8248 }
8249 }
8250 sp = arglist.list;
8251 }
Rob Landley53437472006-07-16 08:14:35 +00008252 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008253 }
8254
8255 cmd_is_exec = 0;
8256 spclbltin = -1;
8257
8258 /* Now locate the command. */
8259 if (argc) {
8260 const char *oldpath;
8261 int cmd_flag = DO_ERR;
8262
8263 path += 5;
8264 oldpath = path;
8265 for (;;) {
8266 find_command(argv[0], &cmdentry, cmd_flag, path);
8267 if (cmdentry.cmdtype == CMDUNKNOWN) {
8268 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008269 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008270 goto bail;
8271 }
8272
8273 /* implement bltin and command here */
8274 if (cmdentry.cmdtype != CMDBUILTIN)
8275 break;
8276 if (spclbltin < 0)
8277 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8278 if (cmdentry.u.cmd == EXECCMD)
8279 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008280#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008281 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008282 path = oldpath;
8283 nargv = parse_command_args(argv, &path);
8284 if (!nargv)
8285 break;
8286 argc -= nargv - argv;
8287 argv = nargv;
8288 cmd_flag |= DO_NOFUNC;
8289 } else
8290#endif
8291 break;
8292 }
8293 }
8294
8295 if (status) {
8296 /* We have a redirection error. */
8297 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008298 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008299 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008300 exitstatus = status;
8301 goto out;
8302 }
8303
8304 /* Execute the command. */
8305 switch (cmdentry.cmdtype) {
8306 default:
8307 /* Fork off a child process if necessary. */
8308 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008309 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008310 jp = makejob(cmd, 1);
8311 if (forkshell(jp, cmd, FORK_FG) != 0) {
8312 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008313 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008314 break;
8315 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008316 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008317 }
8318 listsetvar(varlist.list, VEXPORT|VSTACK);
8319 shellexec(argv, path, cmdentry.u.index);
8320 /* NOTREACHED */
8321
8322 case CMDBUILTIN:
8323 cmdenviron = varlist.list;
8324 if (cmdenviron) {
8325 struct strlist *list = cmdenviron;
8326 int i = VNOSET;
8327 if (spclbltin > 0 || argc == 0) {
8328 i = 0;
8329 if (cmd_is_exec && argc > 1)
8330 i = VEXPORT;
8331 }
8332 listsetvar(list, i);
8333 }
8334 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8335 int exit_status;
8336 int i, j;
8337
8338 i = exception;
8339 if (i == EXEXIT)
8340 goto raise;
8341
8342 exit_status = 2;
8343 j = 0;
8344 if (i == EXINT)
8345 j = SIGINT;
8346 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008347 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008348 if (j)
8349 exit_status = j + 128;
8350 exitstatus = exit_status;
8351
8352 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008353 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008354 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008355 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008356 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008357 }
8358 break;
8359
8360 case CMDFUNCTION:
8361 listsetvar(varlist.list, 0);
8362 if (evalfun(cmdentry.u.func, argc, argv, flags))
8363 goto raise;
8364 break;
8365 }
8366
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008367 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008368 popredir(cmd_is_exec);
8369 if (lastarg)
8370 /* dsl: I think this is intended to be used to support
8371 * '_' in 'vi' command mode during line editing...
8372 * However I implemented that within libedit itself.
8373 */
8374 setvar("_", lastarg, 0);
8375 popstackmark(&smark);
8376}
8377
8378static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008379evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8380{
Eric Andersenc470f442003-07-28 09:56:35 +00008381 char *volatile savecmdname;
8382 struct jmploc *volatile savehandler;
8383 struct jmploc jmploc;
8384 int i;
8385
8386 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008387 i = setjmp(jmploc.loc);
8388 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008389 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008390 savehandler = exception_handler;
8391 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008392 commandname = argv[0];
8393 argptr = argv + 1;
8394 optptr = NULL; /* initialize nextopt */
8395 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008396 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008397 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008398 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008399 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008400 commandname = savecmdname;
8401 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008402 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008403
8404 return i;
8405}
8406
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008407static int
8408goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008409{
8410 return !*endofname(p);
8411}
8412
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008413
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008414/*
8415 * Search for a command. This is called before we fork so that the
8416 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008417 * the child. The check for "goodname" is an overly conservative
8418 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008419 */
Eric Andersenc470f442003-07-28 09:56:35 +00008420static void
8421prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008422{
8423 struct cmdentry entry;
8424
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008425 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8426 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008427}
8428
Eric Andersencb57d552001-06-28 07:25:16 +00008429
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008430/* ============ Builtin commands
8431 *
8432 * Builtin commands whose functions are closely tied to evaluation
8433 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008434 */
8435
8436/*
Eric Andersencb57d552001-06-28 07:25:16 +00008437 * Handle break and continue commands. Break, continue, and return are
8438 * all handled by setting the evalskip flag. The evaluation routines
8439 * above all check this flag, and if it is set they start skipping
8440 * commands rather than executing them. The variable skipcount is
8441 * the number of loops to break/continue, or the number of function
8442 * levels to return. (The latter is always 1.) It should probably
8443 * be an error to break out of more loops than exist, but it isn't
8444 * in the standard shell so we don't make it one here.
8445 */
Eric Andersenc470f442003-07-28 09:56:35 +00008446static int
8447breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008448{
8449 int n = argc > 1 ? number(argv[1]) : 1;
8450
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008451 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008452 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008453 if (n > loopnest)
8454 n = loopnest;
8455 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008456 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008457 skipcount = n;
8458 }
8459 return 0;
8460}
8461
Eric Andersenc470f442003-07-28 09:56:35 +00008462
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008463/* ============ input.c
8464 *
Eric Andersen90898442003-08-06 11:20:52 +00008465 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008466 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008467
Eric Andersenc470f442003-07-28 09:56:35 +00008468#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008469
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008470enum {
8471 INPUT_PUSH_FILE = 1,
8472 INPUT_NOFILE_OK = 2,
8473};
Eric Andersencb57d552001-06-28 07:25:16 +00008474
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008475/*
8476 * NEOF is returned by parsecmd when it encounters an end of file. It
8477 * must be distinct from NULL, so we use the address of a variable that
8478 * happens to be handy.
8479 */
8480static int plinno = 1; /* input line number */
8481/* number of characters left in input buffer */
8482static int parsenleft; /* copy of parsefile->nleft */
8483static int parselleft; /* copy of parsefile->lleft */
8484/* next character in input buffer */
8485static char *parsenextc; /* copy of parsefile->nextc */
8486
8487static int checkkwd;
8488/* values of checkkwd variable */
8489#define CHKALIAS 0x1
8490#define CHKKWD 0x2
8491#define CHKNL 0x4
8492
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008493static void
8494popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008495{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008496 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008497
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008498 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008499#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008500 if (sp->ap) {
8501 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8502 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008503 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008504 if (sp->string != sp->ap->val) {
8505 free(sp->string);
8506 }
8507 sp->ap->flag &= ~ALIASINUSE;
8508 if (sp->ap->flag & ALIASDEAD) {
8509 unalias(sp->ap->name);
8510 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008511 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008512#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008513 parsenextc = sp->prevstring;
8514 parsenleft = sp->prevnleft;
8515/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8516 parsefile->strpush = sp->prev;
8517 if (sp != &(parsefile->basestrpush))
8518 free(sp);
8519 INT_ON;
8520}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008521
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008522static int
8523preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008524{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008525 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008526 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008527 parsenextc = buf;
8528
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008529 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008530#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008531 if (!iflag || parsefile->fd)
8532 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8533 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008534#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008535 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008536#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008537 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8538 if (nr == 0) {
8539 /* Ctrl+C pressed */
8540 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008541 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008542 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008543 raise(SIGINT);
8544 return 1;
8545 }
Eric Andersenc470f442003-07-28 09:56:35 +00008546 goto retry;
8547 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008548 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008549 /* Ctrl+D presend */
8550 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008551 }
Eric Andersencb57d552001-06-28 07:25:16 +00008552 }
8553#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008554 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008555#endif
8556
8557 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008558 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
8559 int flags = fcntl(0, F_GETFL, 0);
8560 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008561 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008562 if (fcntl(0, F_SETFL, flags) >= 0) {
8563 out2str("sh: turning off NDELAY mode\n");
8564 goto retry;
8565 }
8566 }
8567 }
8568 }
8569 return nr;
8570}
8571
8572/*
8573 * Refill the input buffer and return the next input character:
8574 *
8575 * 1) If a string was pushed back on the input, pop it;
8576 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8577 * from a string so we can't refill the buffer, return EOF.
8578 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8579 * 4) Process input up to the next newline, deleting nul characters.
8580 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008581static int
Eric Andersenc470f442003-07-28 09:56:35 +00008582preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008583{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008584 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008585 int more;
8586 char savec;
8587
8588 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008589#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008590 if (parsenleft == -1 && parsefile->strpush->ap &&
8591 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008592 return PEOA;
8593 }
Eric Andersen2870d962001-07-02 17:27:21 +00008594#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008595 popstring();
8596 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008597 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008598 }
8599 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8600 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008601 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008602
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008603 more = parselleft;
8604 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008605 again:
8606 more = preadfd();
8607 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008608 parselleft = parsenleft = EOF_NLEFT;
8609 return PEOF;
8610 }
8611 }
8612
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008613 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008614
8615 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008616 for (;;) {
8617 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008618
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008619 more--;
8620 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008621
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008622 if (!c)
8623 memmove(q, q + 1, more);
8624 else {
8625 q++;
8626 if (c == '\n') {
8627 parsenleft = q - parsenextc - 1;
8628 break;
8629 }
Eric Andersencb57d552001-06-28 07:25:16 +00008630 }
8631
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008632 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008633 parsenleft = q - parsenextc - 1;
8634 if (parsenleft < 0)
8635 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008636 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008637 }
8638 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008639 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008640
8641 savec = *q;
8642 *q = '\0';
8643
8644 if (vflag) {
8645 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008646 }
8647
8648 *q = savec;
8649
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008650 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008651}
8652
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008653#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008654static int
8655pgetc(void)
8656{
8657 return pgetc_as_macro();
8658}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008659
8660#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8661#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008662#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008663#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008664#endif
8665
8666/*
8667 * Same as pgetc(), but ignores PEOA.
8668 */
8669#if ENABLE_ASH_ALIAS
8670static int
8671pgetc2(void)
8672{
8673 int c;
8674
8675 do {
8676 c = pgetc_macro();
8677 } while (c == PEOA);
8678 return c;
8679}
8680#else
8681static int
8682pgetc2(void)
8683{
8684 return pgetc_macro();
8685}
8686#endif
8687
8688/*
8689 * Read a line from the script.
8690 */
8691static char *
8692pfgets(char *line, int len)
8693{
8694 char *p = line;
8695 int nleft = len;
8696 int c;
8697
8698 while (--nleft > 0) {
8699 c = pgetc2();
8700 if (c == PEOF) {
8701 if (p == line)
8702 return NULL;
8703 break;
8704 }
8705 *p++ = c;
8706 if (c == '\n')
8707 break;
8708 }
8709 *p = '\0';
8710 return line;
8711}
8712
Eric Andersenc470f442003-07-28 09:56:35 +00008713/*
8714 * Undo the last call to pgetc. Only one character may be pushed back.
8715 * PEOF may be pushed back.
8716 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008717static void
Eric Andersenc470f442003-07-28 09:56:35 +00008718pungetc(void)
8719{
8720 parsenleft++;
8721 parsenextc--;
8722}
Eric Andersencb57d552001-06-28 07:25:16 +00008723
8724/*
8725 * Push a string back onto the input at this current parsefile level.
8726 * We handle aliases this way.
8727 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008728static void
Eric Andersenc470f442003-07-28 09:56:35 +00008729pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008730{
Eric Andersencb57d552001-06-28 07:25:16 +00008731 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008732 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008733
Eric Andersenc470f442003-07-28 09:56:35 +00008734 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008735 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008736/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8737 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008738 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008739 sp->prev = parsefile->strpush;
8740 parsefile->strpush = sp;
8741 } else
8742 sp = parsefile->strpush = &(parsefile->basestrpush);
8743 sp->prevstring = parsenextc;
8744 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008745#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008746 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008747 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008748 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008749 sp->string = s;
8750 }
Eric Andersen2870d962001-07-02 17:27:21 +00008751#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008752 parsenextc = s;
8753 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008754 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008755}
8756
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008757/*
8758 * To handle the "." command, a stack of input files is used. Pushfile
8759 * adds a new entry to the stack and popfile restores the previous level.
8760 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008761static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008762pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008763{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008764 struct parsefile *pf;
8765
8766 parsefile->nleft = parsenleft;
8767 parsefile->lleft = parselleft;
8768 parsefile->nextc = parsenextc;
8769 parsefile->linno = plinno;
8770 pf = ckmalloc(sizeof(*pf));
8771 pf->prev = parsefile;
8772 pf->fd = -1;
8773 pf->strpush = NULL;
8774 pf->basestrpush.prev = NULL;
8775 parsefile = pf;
8776}
8777
8778static void
8779popfile(void)
8780{
8781 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008782
Denis Vlasenkob012b102007-02-19 22:43:01 +00008783 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008784 if (pf->fd >= 0)
8785 close(pf->fd);
8786 if (pf->buf)
8787 free(pf->buf);
8788 while (pf->strpush)
8789 popstring();
8790 parsefile = pf->prev;
8791 free(pf);
8792 parsenleft = parsefile->nleft;
8793 parselleft = parsefile->lleft;
8794 parsenextc = parsefile->nextc;
8795 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008796 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008797}
8798
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008799/*
8800 * Return to top level.
8801 */
8802static void
8803popallfiles(void)
8804{
8805 while (parsefile != &basepf)
8806 popfile();
8807}
8808
8809/*
8810 * Close the file(s) that the shell is reading commands from. Called
8811 * after a fork is done.
8812 */
8813static void
8814closescript(void)
8815{
8816 popallfiles();
8817 if (parsefile->fd > 0) {
8818 close(parsefile->fd);
8819 parsefile->fd = 0;
8820 }
8821}
8822
8823/*
8824 * Like setinputfile, but takes an open file descriptor. Call this with
8825 * interrupts off.
8826 */
8827static void
8828setinputfd(int fd, int push)
8829{
8830 fcntl(fd, F_SETFD, FD_CLOEXEC);
8831 if (push) {
8832 pushfile();
8833 parsefile->buf = 0;
8834 }
8835 parsefile->fd = fd;
8836 if (parsefile->buf == NULL)
8837 parsefile->buf = ckmalloc(IBUFSIZ);
8838 parselleft = parsenleft = 0;
8839 plinno = 1;
8840}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008841
Eric Andersenc470f442003-07-28 09:56:35 +00008842/*
8843 * Set the input to take input from a file. If push is set, push the
8844 * old input onto the stack first.
8845 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008846static int
8847setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008848{
8849 int fd;
8850 int fd2;
8851
Denis Vlasenkob012b102007-02-19 22:43:01 +00008852 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008853 fd = open(fname, O_RDONLY);
8854 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008855 if (flags & INPUT_NOFILE_OK)
8856 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008857 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008858 }
Eric Andersenc470f442003-07-28 09:56:35 +00008859 if (fd < 10) {
8860 fd2 = copyfd(fd, 10);
8861 close(fd);
8862 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008863 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008864 fd = fd2;
8865 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008866 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008867 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008868 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008869 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008870}
8871
Eric Andersencb57d552001-06-28 07:25:16 +00008872/*
8873 * Like setinputfile, but takes input from a string.
8874 */
Eric Andersenc470f442003-07-28 09:56:35 +00008875static void
8876setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008877{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008878 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008879 pushfile();
8880 parsenextc = string;
8881 parsenleft = strlen(string);
8882 parsefile->buf = NULL;
8883 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008884 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008885}
8886
8887
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008888/* ============ mail.c
8889 *
8890 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008891 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008892
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008893#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008894
Eric Andersencb57d552001-06-28 07:25:16 +00008895#define MAXMBOXES 10
8896
Eric Andersenc470f442003-07-28 09:56:35 +00008897/* times of mailboxes */
8898static time_t mailtime[MAXMBOXES];
8899/* Set if MAIL or MAILPATH is changed. */
8900static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008901
Eric Andersencb57d552001-06-28 07:25:16 +00008902/*
Eric Andersenc470f442003-07-28 09:56:35 +00008903 * Print appropriate message(s) if mail has arrived.
8904 * If mail_var_path_changed is set,
8905 * then the value of MAIL has mail_var_path_changed,
8906 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008907 */
Eric Andersenc470f442003-07-28 09:56:35 +00008908static void
8909chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008910{
Eric Andersencb57d552001-06-28 07:25:16 +00008911 const char *mpath;
8912 char *p;
8913 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008914 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008915 struct stackmark smark;
8916 struct stat statb;
8917
Eric Andersencb57d552001-06-28 07:25:16 +00008918 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008919 mpath = mpathset() ? mpathval() : mailval();
8920 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008921 p = padvance(&mpath, nullstr);
8922 if (p == NULL)
8923 break;
8924 if (*p == '\0')
8925 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008926 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008927#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008928 if (q[-1] != '/')
8929 abort();
8930#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008931 q[-1] = '\0'; /* delete trailing '/' */
8932 if (stat(p, &statb) < 0) {
8933 *mtp = 0;
8934 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008935 }
Eric Andersenc470f442003-07-28 09:56:35 +00008936 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8937 fprintf(
8938 stderr, snlfmt,
8939 pathopt ? pathopt : "you have mail"
8940 );
8941 }
8942 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008943 }
Eric Andersenc470f442003-07-28 09:56:35 +00008944 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008945 popstackmark(&smark);
8946}
Eric Andersencb57d552001-06-28 07:25:16 +00008947
Eric Andersenc470f442003-07-28 09:56:35 +00008948static void
8949changemail(const char *val)
8950{
8951 mail_var_path_changed++;
8952}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008953
Denis Vlasenko131ae172007-02-18 13:00:19 +00008954#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008955
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008956
8957/* ============ ??? */
8958
Eric Andersencb57d552001-06-28 07:25:16 +00008959/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008960 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00008961 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008962static void
8963setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008964{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008965 char **newparam;
8966 char **ap;
8967 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00008968
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008969 for (nparam = 0; argv[nparam]; nparam++);
8970 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
8971 while (*argv) {
8972 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008973 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008974 *ap = NULL;
8975 freeparam(&shellparam);
8976 shellparam.malloc = 1;
8977 shellparam.nparam = nparam;
8978 shellparam.p = newparam;
8979#if ENABLE_ASH_GETOPTS
8980 shellparam.optind = 1;
8981 shellparam.optoff = -1;
8982#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008983}
8984
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008985/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008986 * Process shell options. The global variable argptr contains a pointer
8987 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008988 */
8989static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008990minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008991{
8992 int i;
8993
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008994 if (name) {
8995 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008996 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008997 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00008998 return;
8999 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009000 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009001 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009002 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009003 out1str("Current option settings\n");
9004 for (i = 0; i < NOPTS; i++)
9005 out1fmt("%-16s%s\n", optnames(i),
9006 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009007}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009008static void
9009setoption(int flag, int val)
9010{
9011 int i;
9012
9013 for (i = 0; i < NOPTS; i++) {
9014 if (optletters(i) == flag) {
9015 optlist[i] = val;
9016 return;
9017 }
9018 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009019 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009020 /* NOTREACHED */
9021}
Eric Andersenc470f442003-07-28 09:56:35 +00009022static void
9023options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009024{
9025 char *p;
9026 int val;
9027 int c;
9028
9029 if (cmdline)
9030 minusc = NULL;
9031 while ((p = *argptr) != NULL) {
9032 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009033 c = *p++;
9034 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009035 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009036 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009037 if (!cmdline) {
9038 /* "-" means turn off -x and -v */
9039 if (p[0] == '\0')
9040 xflag = vflag = 0;
9041 /* "--" means reset params */
9042 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009043 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009044 }
Eric Andersenc470f442003-07-28 09:56:35 +00009045 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009046 }
9047 } else if (c == '+') {
9048 val = 0;
9049 } else {
9050 argptr--;
9051 break;
9052 }
9053 while ((c = *p++) != '\0') {
9054 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00009055 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00009056 } else if (c == 'o') {
9057 minus_o(*argptr, val);
9058 if (*argptr)
9059 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00009060 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009061 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009062 isloginsh = 1;
9063 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009064 } else {
9065 setoption(c, val);
9066 }
9067 }
9068 }
9069}
9070
Eric Andersencb57d552001-06-28 07:25:16 +00009071/*
Eric Andersencb57d552001-06-28 07:25:16 +00009072 * The shift builtin command.
9073 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009074static int
Eric Andersenc470f442003-07-28 09:56:35 +00009075shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009076{
9077 int n;
9078 char **ap1, **ap2;
9079
9080 n = 1;
9081 if (argc > 1)
9082 n = number(argv[1]);
9083 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009084 ash_msg_and_raise_error("can't shift that many");
9085 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009086 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009087 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009088 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009089 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009090 }
9091 ap2 = shellparam.p;
9092 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009093#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009094 shellparam.optind = 1;
9095 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009096#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009097 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009098 return 0;
9099}
9100
Eric Andersencb57d552001-06-28 07:25:16 +00009101/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009102 * POSIX requires that 'set' (but not export or readonly) output the
9103 * variables in lexicographic order - by the locale's collating order (sigh).
9104 * Maybe we could keep them in an ordered balanced binary tree
9105 * instead of hashed lists.
9106 * For now just roll 'em through qsort for printing...
9107 */
9108static int
9109showvars(const char *sep_prefix, int on, int off)
9110{
9111 const char *sep;
9112 char **ep, **epend;
9113
9114 ep = listvars(on, off, &epend);
9115 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9116
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009117 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009118
9119 for (; ep < epend; ep++) {
9120 const char *p;
9121 const char *q;
9122
9123 p = strchrnul(*ep, '=');
9124 q = nullstr;
9125 if (*p)
9126 q = single_quote(++p);
9127 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9128 }
9129 return 0;
9130}
9131
9132/*
Eric Andersencb57d552001-06-28 07:25:16 +00009133 * The set command builtin.
9134 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009135static int
Eric Andersenc470f442003-07-28 09:56:35 +00009136setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009137{
9138 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009139 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009140 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009141 options(0);
9142 optschanged();
9143 if (*argptr != NULL) {
9144 setparam(argptr);
9145 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009146 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009147 return 0;
9148}
9149
Denis Vlasenko131ae172007-02-18 13:00:19 +00009150#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009151/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009152static void
9153change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009154{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009155 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009156 /* "get", generate */
9157 char buf[16];
9158
9159 rseed = rseed * 1103515245 + 12345;
9160 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9161 /* set without recursion */
9162 setvar(vrandom.text, buf, VNOFUNC);
9163 vrandom.flags &= ~VNOFUNC;
9164 } else {
9165 /* set/reset */
9166 rseed = strtoul(value, (char **)NULL, 10);
9167 }
Eric Andersenef02f822004-03-11 13:34:24 +00009168}
Eric Andersen16767e22004-03-16 05:14:10 +00009169#endif
9170
Denis Vlasenko131ae172007-02-18 13:00:19 +00009171#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009172static int
Eric Andersenc470f442003-07-28 09:56:35 +00009173getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009174{
9175 char *p, *q;
9176 char c = '?';
9177 int done = 0;
9178 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009179 char s[12];
9180 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009181
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009182 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009183 return 1;
9184 optnext = optfirst + *param_optind - 1;
9185
9186 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009187 p = NULL;
9188 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009189 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009190 if (p == NULL || *p == '\0') {
9191 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009192 p = *optnext;
9193 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009194 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009195 p = NULL;
9196 done = 1;
9197 goto out;
9198 }
9199 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009200 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009201 goto atend;
9202 }
9203
9204 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009205 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009206 if (*q == '\0') {
9207 if (optstr[0] == ':') {
9208 s[0] = c;
9209 s[1] = '\0';
9210 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009211 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009212 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009213 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009214 }
9215 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009216 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009217 }
9218 if (*++q == ':')
9219 q++;
9220 }
9221
9222 if (*++q == ':') {
9223 if (*p == '\0' && (p = *optnext) == NULL) {
9224 if (optstr[0] == ':') {
9225 s[0] = c;
9226 s[1] = '\0';
9227 err |= setvarsafe("OPTARG", s, 0);
9228 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009229 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009230 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009231 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009232 c = '?';
9233 }
Eric Andersenc470f442003-07-28 09:56:35 +00009234 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009235 }
9236
9237 if (p == *optnext)
9238 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009239 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009240 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009241 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009242 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009243 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009244 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009245 *param_optind = optnext - optfirst + 1;
9246 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009247 err |= setvarsafe("OPTIND", s, VNOFUNC);
9248 s[0] = c;
9249 s[1] = '\0';
9250 err |= setvarsafe(optvar, s, 0);
9251 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009252 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009253 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009254 flush_stdout_stderr();
9255 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009256 }
9257 return done;
9258}
Eric Andersenc470f442003-07-28 09:56:35 +00009259
9260/*
9261 * The getopts builtin. Shellparam.optnext points to the next argument
9262 * to be processed. Shellparam.optptr points to the next character to
9263 * be processed in the current argument. If shellparam.optnext is NULL,
9264 * then it's the first time getopts has been called.
9265 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009266static int
Eric Andersenc470f442003-07-28 09:56:35 +00009267getoptscmd(int argc, char **argv)
9268{
9269 char **optbase;
9270
9271 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009272 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009273 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009274 optbase = shellparam.p;
9275 if (shellparam.optind > shellparam.nparam + 1) {
9276 shellparam.optind = 1;
9277 shellparam.optoff = -1;
9278 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009279 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009280 optbase = &argv[3];
9281 if (shellparam.optind > argc - 2) {
9282 shellparam.optind = 1;
9283 shellparam.optoff = -1;
9284 }
9285 }
9286
9287 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009288 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009289}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009290#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009291
Eric Andersencb57d552001-06-28 07:25:16 +00009292
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009293/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009294
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009295static int tokpushback; /* last token pushed back */
9296#define NEOF ((union node *)&tokpushback)
9297static int parsebackquote; /* nonzero if we are inside backquotes */
9298static int lasttoken; /* last token read */
9299static char *wordtext; /* text of last word returned by readtoken */
9300static struct nodelist *backquotelist;
9301static union node *redirnode;
9302static struct heredoc *heredoc;
9303static int quoteflag; /* set if (part of) last token was quoted */
9304
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009305static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9306static void
9307raise_error_syntax(const char *msg)
9308{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009309 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009310 /* NOTREACHED */
9311}
9312
9313/*
9314 * Called when an unexpected token is read during the parse. The argument
9315 * is the token that is expected, or -1 if more than one type of token can
9316 * occur at this point.
9317 */
9318static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9319static void
9320raise_error_unexpected_syntax(int token)
9321{
9322 char msg[64];
9323 int l;
9324
9325 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9326 if (token >= 0)
9327 sprintf(msg + l, " (expecting %s)", tokname(token));
9328 raise_error_syntax(msg);
9329 /* NOTREACHED */
9330}
Eric Andersencb57d552001-06-28 07:25:16 +00009331
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009332#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009333
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009334struct heredoc {
9335 struct heredoc *next; /* next here document in list */
9336 union node *here; /* redirection node */
9337 char *eofmark; /* string indicating end of input */
9338 int striptabs; /* if set, strip leading tabs */
9339};
Eric Andersencb57d552001-06-28 07:25:16 +00009340
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009341static struct heredoc *heredoclist; /* list of here documents to read */
9342
9343/* parsing is heavily cross-recursive, need these forward decls */
9344static union node *andor(void);
9345static union node *pipeline(void);
9346static union node *parse_command(void);
9347static void parseheredoc(void);
9348static char peektoken(void);
9349static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009350
Eric Andersenc470f442003-07-28 09:56:35 +00009351static union node *
9352list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009353{
9354 union node *n1, *n2, *n3;
9355 int tok;
9356
Eric Andersenc470f442003-07-28 09:56:35 +00009357 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9358 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009359 return NULL;
9360 n1 = NULL;
9361 for (;;) {
9362 n2 = andor();
9363 tok = readtoken();
9364 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009365 if (n2->type == NPIPE) {
9366 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009367 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009368 if (n2->type != NREDIR) {
9369 n3 = stalloc(sizeof(struct nredir));
9370 n3->nredir.n = n2;
9371 n3->nredir.redirect = NULL;
9372 n2 = n3;
9373 }
9374 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009375 }
9376 }
9377 if (n1 == NULL) {
9378 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009379 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009380 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009381 n3->type = NSEMI;
9382 n3->nbinary.ch1 = n1;
9383 n3->nbinary.ch2 = n2;
9384 n1 = n3;
9385 }
9386 switch (tok) {
9387 case TBACKGND:
9388 case TSEMI:
9389 tok = readtoken();
9390 /* fall through */
9391 case TNL:
9392 if (tok == TNL) {
9393 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009394 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009395 return n1;
9396 } else {
9397 tokpushback++;
9398 }
Eric Andersenc470f442003-07-28 09:56:35 +00009399 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009400 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009401 return n1;
9402 break;
9403 case TEOF:
9404 if (heredoclist)
9405 parseheredoc();
9406 else
Eric Andersenc470f442003-07-28 09:56:35 +00009407 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009408 return n1;
9409 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009410 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009411 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009412 tokpushback++;
9413 return n1;
9414 }
9415 }
9416}
9417
Eric Andersenc470f442003-07-28 09:56:35 +00009418static union node *
9419andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009420{
Eric Andersencb57d552001-06-28 07:25:16 +00009421 union node *n1, *n2, *n3;
9422 int t;
9423
Eric Andersencb57d552001-06-28 07:25:16 +00009424 n1 = pipeline();
9425 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009426 t = readtoken();
9427 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009428 t = NAND;
9429 } else if (t == TOR) {
9430 t = NOR;
9431 } else {
9432 tokpushback++;
9433 return n1;
9434 }
Eric Andersenc470f442003-07-28 09:56:35 +00009435 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009436 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009437 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009438 n3->type = t;
9439 n3->nbinary.ch1 = n1;
9440 n3->nbinary.ch2 = n2;
9441 n1 = n3;
9442 }
9443}
9444
Eric Andersenc470f442003-07-28 09:56:35 +00009445static union node *
9446pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009447{
Eric Andersencb57d552001-06-28 07:25:16 +00009448 union node *n1, *n2, *pipenode;
9449 struct nodelist *lp, *prev;
9450 int negate;
9451
9452 negate = 0;
9453 TRACE(("pipeline: entered\n"));
9454 if (readtoken() == TNOT) {
9455 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009456 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009457 } else
9458 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009459 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009460 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009461 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009462 pipenode->type = NPIPE;
9463 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009464 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009465 pipenode->npipe.cmdlist = lp;
9466 lp->n = n1;
9467 do {
9468 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009469 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009470 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009471 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009472 prev->next = lp;
9473 } while (readtoken() == TPIPE);
9474 lp->next = NULL;
9475 n1 = pipenode;
9476 }
9477 tokpushback++;
9478 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009479 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009480 n2->type = NNOT;
9481 n2->nnot.com = n1;
9482 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009483 }
9484 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009485}
9486
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009487static union node *
9488makename(void)
9489{
9490 union node *n;
9491
9492 n = stalloc(sizeof(struct narg));
9493 n->type = NARG;
9494 n->narg.next = NULL;
9495 n->narg.text = wordtext;
9496 n->narg.backquote = backquotelist;
9497 return n;
9498}
9499
9500static void
9501fixredir(union node *n, const char *text, int err)
9502{
9503 TRACE(("Fix redir %s %d\n", text, err));
9504 if (!err)
9505 n->ndup.vname = NULL;
9506
9507 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009508 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009509 else if (LONE_DASH(text))
9510 n->ndup.dupfd = -1;
9511 else {
9512 if (err)
9513 raise_error_syntax("Bad fd number");
9514 n->ndup.vname = makename();
9515 }
9516}
9517
9518/*
9519 * Returns true if the text contains nothing to expand (no dollar signs
9520 * or backquotes).
9521 */
9522static int
9523noexpand(char *text)
9524{
9525 char *p;
9526 char c;
9527
9528 p = text;
9529 while ((c = *p++) != '\0') {
9530 if (c == CTLQUOTEMARK)
9531 continue;
9532 if (c == CTLESC)
9533 p++;
9534 else if (SIT(c, BASESYNTAX) == CCTL)
9535 return 0;
9536 }
9537 return 1;
9538}
9539
9540static void
9541parsefname(void)
9542{
9543 union node *n = redirnode;
9544
9545 if (readtoken() != TWORD)
9546 raise_error_unexpected_syntax(-1);
9547 if (n->type == NHERE) {
9548 struct heredoc *here = heredoc;
9549 struct heredoc *p;
9550 int i;
9551
9552 if (quoteflag == 0)
9553 n->type = NXHERE;
9554 TRACE(("Here document %d\n", n->type));
9555 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9556 raise_error_syntax("Illegal eof marker for << redirection");
9557 rmescapes(wordtext);
9558 here->eofmark = wordtext;
9559 here->next = NULL;
9560 if (heredoclist == NULL)
9561 heredoclist = here;
9562 else {
9563 for (p = heredoclist; p->next; p = p->next);
9564 p->next = here;
9565 }
9566 } else if (n->type == NTOFD || n->type == NFROMFD) {
9567 fixredir(n, wordtext, 0);
9568 } else {
9569 n->nfile.fname = makename();
9570 }
9571}
Eric Andersencb57d552001-06-28 07:25:16 +00009572
Eric Andersenc470f442003-07-28 09:56:35 +00009573static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009574simplecmd(void)
9575{
9576 union node *args, **app;
9577 union node *n = NULL;
9578 union node *vars, **vpp;
9579 union node **rpp, *redir;
9580 int savecheckkwd;
9581
9582 args = NULL;
9583 app = &args;
9584 vars = NULL;
9585 vpp = &vars;
9586 redir = NULL;
9587 rpp = &redir;
9588
9589 savecheckkwd = CHKALIAS;
9590 for (;;) {
9591 checkkwd = savecheckkwd;
9592 switch (readtoken()) {
9593 case TWORD:
9594 n = stalloc(sizeof(struct narg));
9595 n->type = NARG;
9596 n->narg.text = wordtext;
9597 n->narg.backquote = backquotelist;
9598 if (savecheckkwd && isassignment(wordtext)) {
9599 *vpp = n;
9600 vpp = &n->narg.next;
9601 } else {
9602 *app = n;
9603 app = &n->narg.next;
9604 savecheckkwd = 0;
9605 }
9606 break;
9607 case TREDIR:
9608 *rpp = n = redirnode;
9609 rpp = &n->nfile.next;
9610 parsefname(); /* read name of redirection file */
9611 break;
9612 case TLP:
9613 if (args && app == &args->narg.next
9614 && !vars && !redir
9615 ) {
9616 struct builtincmd *bcmd;
9617 const char *name;
9618
9619 /* We have a function */
9620 if (readtoken() != TRP)
9621 raise_error_unexpected_syntax(TRP);
9622 name = n->narg.text;
9623 if (!goodname(name)
9624 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9625 ) {
9626 raise_error_syntax("Bad function name");
9627 }
9628 n->type = NDEFUN;
9629 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9630 n->narg.next = parse_command();
9631 return n;
9632 }
9633 /* fall through */
9634 default:
9635 tokpushback++;
9636 goto out;
9637 }
9638 }
9639 out:
9640 *app = NULL;
9641 *vpp = NULL;
9642 *rpp = NULL;
9643 n = stalloc(sizeof(struct ncmd));
9644 n->type = NCMD;
9645 n->ncmd.args = args;
9646 n->ncmd.assign = vars;
9647 n->ncmd.redirect = redir;
9648 return n;
9649}
9650
9651static union node *
9652parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009653{
Eric Andersencb57d552001-06-28 07:25:16 +00009654 union node *n1, *n2;
9655 union node *ap, **app;
9656 union node *cp, **cpp;
9657 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009658 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009659 int t;
9660
9661 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009662 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009663
Eric Andersencb57d552001-06-28 07:25:16 +00009664 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009665 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009666 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009667 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009668 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009669 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009670 n1->type = NIF;
9671 n1->nif.test = list(0);
9672 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009673 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009674 n1->nif.ifpart = list(0);
9675 n2 = n1;
9676 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009677 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009678 n2 = n2->nif.elsepart;
9679 n2->type = NIF;
9680 n2->nif.test = list(0);
9681 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009682 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009683 n2->nif.ifpart = list(0);
9684 }
9685 if (lasttoken == TELSE)
9686 n2->nif.elsepart = list(0);
9687 else {
9688 n2->nif.elsepart = NULL;
9689 tokpushback++;
9690 }
Eric Andersenc470f442003-07-28 09:56:35 +00009691 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009692 break;
9693 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009694 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009695 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009696 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009697 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009698 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009699 got = readtoken();
9700 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009701 TRACE(("expecting DO got %s %s\n", tokname(got),
9702 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009703 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009704 }
9705 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009706 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009707 break;
9708 }
9709 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009710 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009711 raise_error_syntax("Bad for loop variable");
9712 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009713 n1->type = NFOR;
9714 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009715 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009716 if (readtoken() == TIN) {
9717 app = &ap;
9718 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009719 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009720 n2->type = NARG;
9721 n2->narg.text = wordtext;
9722 n2->narg.backquote = backquotelist;
9723 *app = n2;
9724 app = &n2->narg.next;
9725 }
9726 *app = NULL;
9727 n1->nfor.args = ap;
9728 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009729 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009730 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009731 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009732 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009733 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009734 n2->narg.backquote = NULL;
9735 n2->narg.next = NULL;
9736 n1->nfor.args = n2;
9737 /*
9738 * Newline or semicolon here is optional (but note
9739 * that the original Bourne shell only allowed NL).
9740 */
9741 if (lasttoken != TNL && lasttoken != TSEMI)
9742 tokpushback++;
9743 }
Eric Andersenc470f442003-07-28 09:56:35 +00009744 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009745 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009746 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009747 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009748 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009749 break;
9750 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009751 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009752 n1->type = NCASE;
9753 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009754 raise_error_unexpected_syntax(TWORD);
9755 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009756 n2->type = NARG;
9757 n2->narg.text = wordtext;
9758 n2->narg.backquote = backquotelist;
9759 n2->narg.next = NULL;
9760 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009761 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009762 } while (readtoken() == TNL);
9763 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009764 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009765 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009766 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009767 checkkwd = CHKNL | CHKKWD;
9768 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009769 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009770 if (lasttoken == TLP)
9771 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009772 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009773 cp->type = NCLIST;
9774 app = &cp->nclist.pattern;
9775 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009776 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009777 ap->type = NARG;
9778 ap->narg.text = wordtext;
9779 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009780 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009781 break;
9782 app = &ap->narg.next;
9783 readtoken();
9784 }
9785 ap->narg.next = NULL;
9786 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009787 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009788 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009789
Eric Andersenc470f442003-07-28 09:56:35 +00009790 cpp = &cp->nclist.next;
9791
9792 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009793 t = readtoken();
9794 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009795 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009796 raise_error_unexpected_syntax(TENDCASE);
9797 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009798 }
Eric Andersenc470f442003-07-28 09:56:35 +00009799 }
Eric Andersencb57d552001-06-28 07:25:16 +00009800 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009801 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009802 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009803 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009804 n1->type = NSUBSHELL;
9805 n1->nredir.n = list(0);
9806 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009807 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009808 break;
9809 case TBEGIN:
9810 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009811 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009812 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009813 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009814 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009815 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009816 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009817 }
9818
Eric Andersenc470f442003-07-28 09:56:35 +00009819 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009820 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009821
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009822 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009823 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009824 checkkwd = CHKKWD | CHKALIAS;
9825 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009826 while (readtoken() == TREDIR) {
9827 *rpp = n2 = redirnode;
9828 rpp = &n2->nfile.next;
9829 parsefname();
9830 }
9831 tokpushback++;
9832 *rpp = NULL;
9833 if (redir) {
9834 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009835 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009836 n2->type = NREDIR;
9837 n2->nredir.n = n1;
9838 n1 = n2;
9839 }
9840 n1->nredir.redirect = redir;
9841 }
Eric Andersencb57d552001-06-28 07:25:16 +00009842 return n1;
9843}
9844
Eric Andersencb57d552001-06-28 07:25:16 +00009845/*
9846 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9847 * is not NULL, read a here document. In the latter case, eofmark is the
9848 * word which marks the end of the document and striptabs is true if
9849 * leading tabs should be stripped from the document. The argument firstc
9850 * is the first character of the input token or document.
9851 *
9852 * Because C does not have internal subroutines, I have simulated them
9853 * using goto's to implement the subroutine linkage. The following macros
9854 * will run code that appears at the end of readtoken1.
9855 */
9856
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009857static int parsebackquote; /* nonzero if we are inside backquotes */
9858
Eric Andersen2870d962001-07-02 17:27:21 +00009859#define CHECKEND() {goto checkend; checkend_return:;}
9860#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9861#define PARSESUB() {goto parsesub; parsesub_return:;}
9862#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9863#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9864#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009865
9866static int
Eric Andersenc470f442003-07-28 09:56:35 +00009867readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009868{
Eric Andersencb57d552001-06-28 07:25:16 +00009869 int c = firstc;
9870 char *out;
9871 int len;
9872 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009873 struct nodelist *bqlist = 0;
9874 int quotef = 0;
9875 int dblquote = 0;
9876 int varnest = 0; /* levels of variables expansion */
9877 int arinest = 0; /* levels of arithmetic expansion */
9878 int parenlevel = 0; /* levels of parens in arithmetic */
9879 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9880 int oldstyle = 0;
9881 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009882#if __GNUC__
9883 /* Avoid longjmp clobbering */
9884 (void) &out;
9885 (void) &quotef;
9886 (void) &dblquote;
9887 (void) &varnest;
9888 (void) &arinest;
9889 (void) &parenlevel;
9890 (void) &dqvarnest;
9891 (void) &oldstyle;
9892 (void) &prevsyntax;
9893 (void) &syntax;
9894#endif
9895
9896 startlinno = plinno;
9897 dblquote = 0;
9898 if (syntax == DQSYNTAX)
9899 dblquote = 1;
9900 quotef = 0;
9901 bqlist = NULL;
9902 varnest = 0;
9903 arinest = 0;
9904 parenlevel = 0;
9905 dqvarnest = 0;
9906
9907 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009908 loop: { /* for each line, until end of word */
9909 CHECKEND(); /* set c to PEOF if at end of here document */
9910 for (;;) { /* until end of line or end of word */
9911 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009912 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009913 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009914 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009915 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009916 USTPUTC(c, out);
9917 plinno++;
9918 if (doprompt)
9919 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009920 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009921 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009922 case CWORD:
9923 USTPUTC(c, out);
9924 break;
9925 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009926 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009927 USTPUTC(CTLESC, out);
9928 USTPUTC(c, out);
9929 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009930 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009931 c = pgetc2();
9932 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009933 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009934 USTPUTC('\\', out);
9935 pungetc();
9936 } else if (c == '\n') {
9937 if (doprompt)
9938 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009939 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009940 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009941 c != '\\' && c != '`' &&
9942 c != '$' && (
9943 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009944 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009945 ) {
9946 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009947 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009948 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009949 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009950 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009951 USTPUTC(c, out);
9952 quotef++;
9953 }
9954 break;
9955 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009956 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009957 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009958 if (eofmark == NULL) {
9959 USTPUTC(CTLQUOTEMARK, out);
9960 }
Eric Andersencb57d552001-06-28 07:25:16 +00009961 break;
9962 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009963 syntax = DQSYNTAX;
9964 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009965 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009966 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009967 if (eofmark != NULL && arinest == 0
9968 && varnest == 0
9969 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009970 USTPUTC(c, out);
9971 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009972 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009973 syntax = BASESYNTAX;
9974 dblquote = 0;
9975 }
9976 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +00009977 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009978 }
9979 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009980 case CVAR: /* '$' */
9981 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +00009982 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009983 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +00009984 if (varnest > 0) {
9985 varnest--;
9986 if (dqvarnest > 0) {
9987 dqvarnest--;
9988 }
9989 USTPUTC(CTLENDVAR, out);
9990 } else {
9991 USTPUTC(c, out);
9992 }
9993 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009994#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00009995 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009996 parenlevel++;
9997 USTPUTC(c, out);
9998 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009999 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010000 if (parenlevel > 0) {
10001 USTPUTC(c, out);
10002 --parenlevel;
10003 } else {
10004 if (pgetc() == ')') {
10005 if (--arinest == 0) {
10006 USTPUTC(CTLENDARI, out);
10007 syntax = prevsyntax;
10008 if (syntax == DQSYNTAX)
10009 dblquote = 1;
10010 else
10011 dblquote = 0;
10012 } else
10013 USTPUTC(')', out);
10014 } else {
10015 /*
10016 * unbalanced parens
10017 * (don't 2nd guess - no error)
10018 */
10019 pungetc();
10020 USTPUTC(')', out);
10021 }
10022 }
10023 break;
10024#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010025 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010026 PARSEBACKQOLD();
10027 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010028 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010029 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010030 case CIGN:
10031 break;
10032 default:
10033 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010034 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010035#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010036 if (c != PEOA)
10037#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010038 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010039
Eric Andersencb57d552001-06-28 07:25:16 +000010040 }
10041 c = pgetc_macro();
10042 }
10043 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010044 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010045#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010046 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010047 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010048#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010049 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010050 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010051 if (varnest != 0) {
10052 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010053 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010054 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010055 }
10056 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010057 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010058 out = stackblock();
10059 if (eofmark == NULL) {
10060 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010061 && quotef == 0
10062 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010063 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010064 PARSEREDIR();
10065 return lasttoken = TREDIR;
10066 } else {
10067 pungetc();
10068 }
10069 }
10070 quoteflag = quotef;
10071 backquotelist = bqlist;
10072 grabstackblock(len);
10073 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010074 lasttoken = TWORD;
10075 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010076/* end of readtoken routine */
10077
Eric Andersencb57d552001-06-28 07:25:16 +000010078/*
10079 * Check to see whether we are at the end of the here document. When this
10080 * is called, c is set to the first character of the next input line. If
10081 * we are at the end of the here document, this routine sets the c to PEOF.
10082 */
Eric Andersenc470f442003-07-28 09:56:35 +000010083checkend: {
10084 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010085#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010086 if (c == PEOA) {
10087 c = pgetc2();
10088 }
10089#endif
10090 if (striptabs) {
10091 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010092 c = pgetc2();
10093 }
Eric Andersenc470f442003-07-28 09:56:35 +000010094 }
10095 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010096 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010097 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010098
Eric Andersenc470f442003-07-28 09:56:35 +000010099 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010100 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010101 if (*p == '\n' && *q == '\0') {
10102 c = PEOF;
10103 plinno++;
10104 needprompt = doprompt;
10105 } else {
10106 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010107 }
10108 }
10109 }
10110 }
Eric Andersenc470f442003-07-28 09:56:35 +000010111 goto checkend_return;
10112}
Eric Andersencb57d552001-06-28 07:25:16 +000010113
Eric Andersencb57d552001-06-28 07:25:16 +000010114/*
10115 * Parse a redirection operator. The variable "out" points to a string
10116 * specifying the fd to be redirected. The variable "c" contains the
10117 * first character of the redirection operator.
10118 */
Eric Andersenc470f442003-07-28 09:56:35 +000010119parseredir: {
10120 char fd = *out;
10121 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010122
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010123 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010124 if (c == '>') {
10125 np->nfile.fd = 1;
10126 c = pgetc();
10127 if (c == '>')
10128 np->type = NAPPEND;
10129 else if (c == '|')
10130 np->type = NCLOBBER;
10131 else if (c == '&')
10132 np->type = NTOFD;
10133 else {
10134 np->type = NTO;
10135 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010136 }
Eric Andersenc470f442003-07-28 09:56:35 +000010137 } else { /* c == '<' */
10138 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010139 c = pgetc();
10140 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010141 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010142 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010143 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010144 np->nfile.fd = 0;
10145 }
10146 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010147 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010148 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010149 c = pgetc();
10150 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010151 heredoc->striptabs = 1;
10152 } else {
10153 heredoc->striptabs = 0;
10154 pungetc();
10155 }
10156 break;
10157
10158 case '&':
10159 np->type = NFROMFD;
10160 break;
10161
10162 case '>':
10163 np->type = NFROMTO;
10164 break;
10165
10166 default:
10167 np->type = NFROM;
10168 pungetc();
10169 break;
10170 }
Eric Andersencb57d552001-06-28 07:25:16 +000010171 }
Eric Andersenc470f442003-07-28 09:56:35 +000010172 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010173 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010174 redirnode = np;
10175 goto parseredir_return;
10176}
Eric Andersencb57d552001-06-28 07:25:16 +000010177
Eric Andersencb57d552001-06-28 07:25:16 +000010178/*
10179 * Parse a substitution. At this point, we have read the dollar sign
10180 * and nothing else.
10181 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010182
10183/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10184 * (assuming ascii char codes, as the original implementation did) */
10185#define is_special(c) \
10186 ((((unsigned int)c) - 33 < 32) \
10187 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010188parsesub: {
10189 int subtype;
10190 int typeloc;
10191 int flags;
10192 char *p;
10193 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010194
Eric Andersenc470f442003-07-28 09:56:35 +000010195 c = pgetc();
10196 if (
10197 c <= PEOA_OR_PEOF ||
10198 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10199 ) {
10200 USTPUTC('$', out);
10201 pungetc();
10202 } else if (c == '(') { /* $(command) or $((arith)) */
10203 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010204#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010205 PARSEARITH();
10206#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010207 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010208#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010209 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010210 pungetc();
10211 PARSEBACKQNEW();
10212 }
10213 } else {
10214 USTPUTC(CTLVAR, out);
10215 typeloc = out - (char *)stackblock();
10216 USTPUTC(VSNORMAL, out);
10217 subtype = VSNORMAL;
10218 if (c == '{') {
10219 c = pgetc();
10220 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010221 c = pgetc();
10222 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010223 c = '#';
10224 else
10225 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010226 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010227 subtype = 0;
10228 }
10229 if (c > PEOA_OR_PEOF && is_name(c)) {
10230 do {
10231 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010232 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010233 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010234 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010235 do {
10236 STPUTC(c, out);
10237 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010238 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010239 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010240 USTPUTC(c, out);
10241 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010242 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010243 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010244
Eric Andersenc470f442003-07-28 09:56:35 +000010245 STPUTC('=', out);
10246 flags = 0;
10247 if (subtype == 0) {
10248 switch (c) {
10249 case ':':
10250 flags = VSNUL;
10251 c = pgetc();
10252 /*FALLTHROUGH*/
10253 default:
10254 p = strchr(types, c);
10255 if (p == NULL)
10256 goto badsub;
10257 subtype = p - types + VSNORMAL;
10258 break;
10259 case '%':
10260 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010261 {
10262 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010263 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010264 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010265 c = pgetc();
10266 if (c == cc)
10267 subtype++;
10268 else
10269 pungetc();
10270 break;
10271 }
10272 }
Eric Andersenc470f442003-07-28 09:56:35 +000010273 } else {
10274 pungetc();
10275 }
10276 if (dblquote || arinest)
10277 flags |= VSQUOTE;
10278 *((char *)stackblock() + typeloc) = subtype | flags;
10279 if (subtype != VSNORMAL) {
10280 varnest++;
10281 if (dblquote || arinest) {
10282 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010283 }
10284 }
10285 }
Eric Andersenc470f442003-07-28 09:56:35 +000010286 goto parsesub_return;
10287}
Eric Andersencb57d552001-06-28 07:25:16 +000010288
Eric Andersencb57d552001-06-28 07:25:16 +000010289/*
10290 * Called to parse command substitutions. Newstyle is set if the command
10291 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10292 * list of commands (passed by reference), and savelen is the number of
10293 * characters on the top of the stack which must be preserved.
10294 */
Eric Andersenc470f442003-07-28 09:56:35 +000010295parsebackq: {
10296 struct nodelist **nlpp;
10297 int savepbq;
10298 union node *n;
10299 char *volatile str;
10300 struct jmploc jmploc;
10301 struct jmploc *volatile savehandler;
10302 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010303 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010304#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010305 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010306#endif
10307
Eric Andersenc470f442003-07-28 09:56:35 +000010308 savepbq = parsebackquote;
10309 if (setjmp(jmploc.loc)) {
10310 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010311 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010312 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010313 exception_handler = savehandler;
10314 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010315 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010316 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010317 str = NULL;
10318 savelen = out - (char *)stackblock();
10319 if (savelen > 0) {
10320 str = ckmalloc(savelen);
10321 memcpy(str, stackblock(), savelen);
10322 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010323 savehandler = exception_handler;
10324 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010325 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010326 if (oldstyle) {
10327 /* We must read until the closing backquote, giving special
10328 treatment to some slashes, and then push the string and
10329 reread it as input, interpreting it normally. */
10330 char *pout;
10331 int pc;
10332 size_t psavelen;
10333 char *pstr;
10334
10335
10336 STARTSTACKSTR(pout);
10337 for (;;) {
10338 if (needprompt) {
10339 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010340 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010341 pc = pgetc();
10342 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010343 case '`':
10344 goto done;
10345
10346 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010347 pc = pgetc();
10348 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010349 plinno++;
10350 if (doprompt)
10351 setprompt(2);
10352 /*
10353 * If eating a newline, avoid putting
10354 * the newline into the new character
10355 * stream (via the STPUTC after the
10356 * switch).
10357 */
10358 continue;
10359 }
10360 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010361 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010362 STPUTC('\\', pout);
10363 if (pc > PEOA_OR_PEOF) {
10364 break;
10365 }
10366 /* fall through */
10367
10368 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010369#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010370 case PEOA:
10371#endif
10372 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010373 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010374
10375 case '\n':
10376 plinno++;
10377 needprompt = doprompt;
10378 break;
10379
10380 default:
10381 break;
10382 }
10383 STPUTC(pc, pout);
10384 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010385 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010386 STPUTC('\0', pout);
10387 psavelen = pout - (char *)stackblock();
10388 if (psavelen > 0) {
10389 pstr = grabstackstr(pout);
10390 setinputstring(pstr);
10391 }
10392 }
10393 nlpp = &bqlist;
10394 while (*nlpp)
10395 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010396 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010397 (*nlpp)->next = NULL;
10398 parsebackquote = oldstyle;
10399
10400 if (oldstyle) {
10401 saveprompt = doprompt;
10402 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010403 }
10404
Eric Andersenc470f442003-07-28 09:56:35 +000010405 n = list(2);
10406
10407 if (oldstyle)
10408 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010409 else if (readtoken() != TRP)
10410 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010411
10412 (*nlpp)->n = n;
10413 if (oldstyle) {
10414 /*
10415 * Start reading from old file again, ignoring any pushed back
10416 * tokens left from the backquote parsing
10417 */
10418 popfile();
10419 tokpushback = 0;
10420 }
10421 while (stackblocksize() <= savelen)
10422 growstackblock();
10423 STARTSTACKSTR(out);
10424 if (str) {
10425 memcpy(out, str, savelen);
10426 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010427 INT_OFF;
10428 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010429 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010430 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010431 }
10432 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010433 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010434 if (arinest || dblquote)
10435 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10436 else
10437 USTPUTC(CTLBACKQ, out);
10438 if (oldstyle)
10439 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010440 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010441}
10442
Denis Vlasenko131ae172007-02-18 13:00:19 +000010443#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010444/*
10445 * Parse an arithmetic expansion (indicate start of one and set state)
10446 */
Eric Andersenc470f442003-07-28 09:56:35 +000010447parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010448 if (++arinest == 1) {
10449 prevsyntax = syntax;
10450 syntax = ARISYNTAX;
10451 USTPUTC(CTLARI, out);
10452 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010453 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010454 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010455 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010456 } else {
10457 /*
10458 * we collapse embedded arithmetic expansion to
10459 * parenthesis, which should be equivalent
10460 */
10461 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010462 }
Eric Andersenc470f442003-07-28 09:56:35 +000010463 goto parsearith_return;
10464}
10465#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010466
Eric Andersenc470f442003-07-28 09:56:35 +000010467} /* end of readtoken */
10468
Eric Andersencb57d552001-06-28 07:25:16 +000010469/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010470 * Read the next input token.
10471 * If the token is a word, we set backquotelist to the list of cmds in
10472 * backquotes. We set quoteflag to true if any part of the word was
10473 * quoted.
10474 * If the token is TREDIR, then we set redirnode to a structure containing
10475 * the redirection.
10476 * In all cases, the variable startlinno is set to the number of the line
10477 * on which the token starts.
10478 *
10479 * [Change comment: here documents and internal procedures]
10480 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10481 * word parsing code into a separate routine. In this case, readtoken
10482 * doesn't need to have any internal procedures, but parseword does.
10483 * We could also make parseoperator in essence the main routine, and
10484 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010485 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010486#define NEW_xxreadtoken
10487#ifdef NEW_xxreadtoken
10488/* singles must be first! */
10489static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010490
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010491static const char xxreadtoken_tokens[] = {
10492 TNL, TLP, TRP, /* only single occurrence allowed */
10493 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10494 TEOF, /* corresponds to trailing nul */
10495 TAND, TOR, TENDCASE, /* if double occurrence */
10496};
10497
10498#define xxreadtoken_doubles \
10499 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10500#define xxreadtoken_singles \
10501 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10502
10503static int
10504xxreadtoken(void)
10505{
10506 int c;
10507
10508 if (tokpushback) {
10509 tokpushback = 0;
10510 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010511 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010512 if (needprompt) {
10513 setprompt(2);
10514 }
10515 startlinno = plinno;
10516 for (;;) { /* until token or start of word found */
10517 c = pgetc_macro();
10518
10519 if ((c != ' ') && (c != '\t')
10520#if ENABLE_ASH_ALIAS
10521 && (c != PEOA)
10522#endif
10523 ) {
10524 if (c == '#') {
10525 while ((c = pgetc()) != '\n' && c != PEOF);
10526 pungetc();
10527 } else if (c == '\\') {
10528 if (pgetc() != '\n') {
10529 pungetc();
10530 goto READTOKEN1;
10531 }
10532 startlinno = ++plinno;
10533 if (doprompt)
10534 setprompt(2);
10535 } else {
10536 const char *p
10537 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10538
10539 if (c != PEOF) {
10540 if (c == '\n') {
10541 plinno++;
10542 needprompt = doprompt;
10543 }
10544
10545 p = strchr(xxreadtoken_chars, c);
10546 if (p == NULL) {
10547 READTOKEN1:
10548 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10549 }
10550
10551 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10552 if (pgetc() == *p) { /* double occurrence? */
10553 p += xxreadtoken_doubles + 1;
10554 } else {
10555 pungetc();
10556 }
10557 }
10558 }
10559 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10560 }
10561 }
10562 } /* for */
10563}
10564#else
10565#define RETURN(token) return lasttoken = token
10566static int
10567xxreadtoken(void)
10568{
10569 int c;
10570
10571 if (tokpushback) {
10572 tokpushback = 0;
10573 return lasttoken;
10574 }
10575 if (needprompt) {
10576 setprompt(2);
10577 }
10578 startlinno = plinno;
10579 for (;;) { /* until token or start of word found */
10580 c = pgetc_macro();
10581 switch (c) {
10582 case ' ': case '\t':
10583#if ENABLE_ASH_ALIAS
10584 case PEOA:
10585#endif
10586 continue;
10587 case '#':
10588 while ((c = pgetc()) != '\n' && c != PEOF);
10589 pungetc();
10590 continue;
10591 case '\\':
10592 if (pgetc() == '\n') {
10593 startlinno = ++plinno;
10594 if (doprompt)
10595 setprompt(2);
10596 continue;
10597 }
10598 pungetc();
10599 goto breakloop;
10600 case '\n':
10601 plinno++;
10602 needprompt = doprompt;
10603 RETURN(TNL);
10604 case PEOF:
10605 RETURN(TEOF);
10606 case '&':
10607 if (pgetc() == '&')
10608 RETURN(TAND);
10609 pungetc();
10610 RETURN(TBACKGND);
10611 case '|':
10612 if (pgetc() == '|')
10613 RETURN(TOR);
10614 pungetc();
10615 RETURN(TPIPE);
10616 case ';':
10617 if (pgetc() == ';')
10618 RETURN(TENDCASE);
10619 pungetc();
10620 RETURN(TSEMI);
10621 case '(':
10622 RETURN(TLP);
10623 case ')':
10624 RETURN(TRP);
10625 default:
10626 goto breakloop;
10627 }
10628 }
10629 breakloop:
10630 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10631#undef RETURN
10632}
10633#endif /* NEW_xxreadtoken */
10634
10635static int
10636readtoken(void)
10637{
10638 int t;
10639#if DEBUG
10640 int alreadyseen = tokpushback;
10641#endif
10642
10643#if ENABLE_ASH_ALIAS
10644 top:
10645#endif
10646
10647 t = xxreadtoken();
10648
10649 /*
10650 * eat newlines
10651 */
10652 if (checkkwd & CHKNL) {
10653 while (t == TNL) {
10654 parseheredoc();
10655 t = xxreadtoken();
10656 }
10657 }
10658
10659 if (t != TWORD || quoteflag) {
10660 goto out;
10661 }
10662
10663 /*
10664 * check for keywords
10665 */
10666 if (checkkwd & CHKKWD) {
10667 const char *const *pp;
10668
10669 pp = findkwd(wordtext);
10670 if (pp) {
10671 lasttoken = t = pp - tokname_array;
10672 TRACE(("keyword %s recognized\n", tokname(t)));
10673 goto out;
10674 }
10675 }
10676
10677 if (checkkwd & CHKALIAS) {
10678#if ENABLE_ASH_ALIAS
10679 struct alias *ap;
10680 ap = lookupalias(wordtext, 1);
10681 if (ap != NULL) {
10682 if (*ap->val) {
10683 pushstring(ap->val, ap);
10684 }
10685 goto top;
10686 }
10687#endif
10688 }
10689 out:
10690 checkkwd = 0;
10691#if DEBUG
10692 if (!alreadyseen)
10693 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10694 else
10695 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10696#endif
10697 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010698}
10699
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010700static char
10701peektoken(void)
10702{
10703 int t;
10704
10705 t = readtoken();
10706 tokpushback++;
10707 return tokname_array[t][0];
10708}
Eric Andersencb57d552001-06-28 07:25:16 +000010709
10710/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010711 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10712 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010713 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010714static union node *
10715parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010716{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010717 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010718
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010719 tokpushback = 0;
10720 doprompt = interact;
10721 if (doprompt)
10722 setprompt(doprompt);
10723 needprompt = 0;
10724 t = readtoken();
10725 if (t == TEOF)
10726 return NEOF;
10727 if (t == TNL)
10728 return NULL;
10729 tokpushback++;
10730 return list(1);
10731}
10732
10733/*
10734 * Input any here documents.
10735 */
10736static void
10737parseheredoc(void)
10738{
10739 struct heredoc *here;
10740 union node *n;
10741
10742 here = heredoclist;
10743 heredoclist = 0;
10744
10745 while (here) {
10746 if (needprompt) {
10747 setprompt(2);
10748 }
10749 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10750 here->eofmark, here->striptabs);
10751 n = stalloc(sizeof(struct narg));
10752 n->narg.type = NARG;
10753 n->narg.next = NULL;
10754 n->narg.text = wordtext;
10755 n->narg.backquote = backquotelist;
10756 here->here->nhere.doc = n;
10757 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010758 }
Eric Andersencb57d552001-06-28 07:25:16 +000010759}
10760
10761
10762/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010763 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010764 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010765#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010766static const char *
10767expandstr(const char *ps)
10768{
10769 union node n;
10770
10771 /* XXX Fix (char *) cast. */
10772 setinputstring((char *)ps);
10773 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10774 popfile();
10775
10776 n.narg.type = NARG;
10777 n.narg.next = NULL;
10778 n.narg.text = wordtext;
10779 n.narg.backquote = backquotelist;
10780
10781 expandarg(&n, NULL, 0);
10782 return stackblock();
10783}
10784#endif
10785
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010786/*
10787 * Execute a command or commands contained in a string.
10788 */
10789static int
10790evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010791{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010792 union node *n;
10793 struct stackmark smark;
10794 int skip;
10795
10796 setinputstring(s);
10797 setstackmark(&smark);
10798
10799 skip = 0;
10800 while ((n = parsecmd(0)) != NEOF) {
10801 evaltree(n, 0);
10802 popstackmark(&smark);
10803 skip = evalskip;
10804 if (skip)
10805 break;
10806 }
10807 popfile();
10808
10809 skip &= mask;
10810 evalskip = skip;
10811 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010812}
10813
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010814/*
10815 * The eval command.
10816 */
10817static int
10818evalcmd(int argc, char **argv)
10819{
10820 char *p;
10821 char *concat;
10822 char **ap;
10823
10824 if (argc > 1) {
10825 p = argv[1];
10826 if (argc > 2) {
10827 STARTSTACKSTR(concat);
10828 ap = argv + 2;
10829 for (;;) {
10830 concat = stack_putstr(p, concat);
10831 p = *ap++;
10832 if (p == NULL)
10833 break;
10834 STPUTC(' ', concat);
10835 }
10836 STPUTC('\0', concat);
10837 p = grabstackstr(concat);
10838 }
10839 evalstring(p, ~SKIPEVAL);
10840
10841 }
10842 return exitstatus;
10843}
10844
10845/*
10846 * Read and execute commands. "Top" is nonzero for the top level command
10847 * loop; it turns on prompting if the shell is interactive.
10848 */
10849static int
10850cmdloop(int top)
10851{
10852 union node *n;
10853 struct stackmark smark;
10854 int inter;
10855 int numeof = 0;
10856
10857 TRACE(("cmdloop(%d) called\n", top));
10858 for (;;) {
10859 int skip;
10860
10861 setstackmark(&smark);
10862#if JOBS
10863 if (jobctl)
10864 showjobs(stderr, SHOW_CHANGED);
10865#endif
10866 inter = 0;
10867 if (iflag && top) {
10868 inter++;
10869#if ENABLE_ASH_MAIL
10870 chkmail();
10871#endif
10872 }
10873 n = parsecmd(inter);
10874 /* showtree(n); DEBUG */
10875 if (n == NEOF) {
10876 if (!top || numeof >= 50)
10877 break;
10878 if (!stoppedjobs()) {
10879 if (!Iflag)
10880 break;
10881 out2str("\nUse \"exit\" to leave shell.\n");
10882 }
10883 numeof++;
10884 } else if (nflag == 0) {
10885 job_warning = (job_warning == 2) ? 1 : 0;
10886 numeof = 0;
10887 evaltree(n, 0);
10888 }
10889 popstackmark(&smark);
10890 skip = evalskip;
10891
10892 if (skip) {
10893 evalskip = 0;
10894 return skip & SKIPEVAL;
10895 }
10896 }
10897 return 0;
10898}
10899
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010900/*
10901 * Take commands from a file. To be compatible we should do a path
10902 * search for the file, which is necessary to find sub-commands.
10903 */
10904static char *
10905find_dot_file(char *name)
10906{
10907 char *fullname;
10908 const char *path = pathval();
10909 struct stat statb;
10910
10911 /* don't try this for absolute or relative paths */
10912 if (strchr(name, '/'))
10913 return name;
10914
10915 while ((fullname = padvance(&path, name)) != NULL) {
10916 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10917 /*
10918 * Don't bother freeing here, since it will
10919 * be freed by the caller.
10920 */
10921 return fullname;
10922 }
10923 stunalloc(fullname);
10924 }
10925
10926 /* not found in the PATH */
10927 ash_msg_and_raise_error("%s: not found", name);
10928 /* NOTREACHED */
10929}
10930
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010931static int
10932dotcmd(int argc, char **argv)
10933{
10934 struct strlist *sp;
10935 volatile struct shparam saveparam;
10936 int status = 0;
10937
10938 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000010939 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010940
10941 if (argc >= 2) { /* That's what SVR2 does */
10942 char *fullname;
10943
10944 fullname = find_dot_file(argv[1]);
10945
10946 if (argc > 2) {
10947 saveparam = shellparam;
10948 shellparam.malloc = 0;
10949 shellparam.nparam = argc - 2;
10950 shellparam.p = argv + 2;
10951 };
10952
10953 setinputfile(fullname, INPUT_PUSH_FILE);
10954 commandname = fullname;
10955 cmdloop(0);
10956 popfile();
10957
10958 if (argc > 2) {
10959 freeparam(&shellparam);
10960 shellparam = saveparam;
10961 };
10962 status = exitstatus;
10963 }
10964 return status;
10965}
10966
10967static int
10968exitcmd(int argc, char **argv)
10969{
10970 if (stoppedjobs())
10971 return 0;
10972 if (argc > 1)
10973 exitstatus = number(argv[1]);
10974 raise_exception(EXEXIT);
10975 /* NOTREACHED */
10976}
10977
10978#if ENABLE_ASH_BUILTIN_ECHO
10979static int
10980echocmd(int argc, char **argv)
10981{
10982 return bb_echo(argv);
10983}
10984#endif
10985
10986#if ENABLE_ASH_BUILTIN_TEST
10987static int
10988testcmd(int argc, char **argv)
10989{
10990 return bb_test(argc, argv);
10991}
10992#endif
10993
10994/*
10995 * Read a file containing shell functions.
10996 */
10997static void
10998readcmdfile(char *name)
10999{
11000 setinputfile(name, INPUT_PUSH_FILE);
11001 cmdloop(0);
11002 popfile();
11003}
11004
11005
Denis Vlasenkocc571512007-02-23 21:10:35 +000011006/* ============ find_command inplementation */
11007
11008/*
11009 * Resolve a command name. If you change this routine, you may have to
11010 * change the shellexec routine as well.
11011 */
11012static void
11013find_command(char *name, struct cmdentry *entry, int act, const char *path)
11014{
11015 struct tblentry *cmdp;
11016 int idx;
11017 int prev;
11018 char *fullname;
11019 struct stat statb;
11020 int e;
11021 int updatetbl;
11022 struct builtincmd *bcmd;
11023
11024 /* If name contains a slash, don't use PATH or hash table */
11025 if (strchr(name, '/') != NULL) {
11026 entry->u.index = -1;
11027 if (act & DO_ABS) {
11028 while (stat(name, &statb) < 0) {
11029#ifdef SYSV
11030 if (errno == EINTR)
11031 continue;
11032#endif
11033 entry->cmdtype = CMDUNKNOWN;
11034 return;
11035 }
11036 }
11037 entry->cmdtype = CMDNORMAL;
11038 return;
11039 }
11040
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011041/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011042
11043 updatetbl = (path == pathval());
11044 if (!updatetbl) {
11045 act |= DO_ALTPATH;
11046 if (strstr(path, "%builtin") != NULL)
11047 act |= DO_ALTBLTIN;
11048 }
11049
11050 /* If name is in the table, check answer will be ok */
11051 cmdp = cmdlookup(name, 0);
11052 if (cmdp != NULL) {
11053 int bit;
11054
11055 switch (cmdp->cmdtype) {
11056 default:
11057#if DEBUG
11058 abort();
11059#endif
11060 case CMDNORMAL:
11061 bit = DO_ALTPATH;
11062 break;
11063 case CMDFUNCTION:
11064 bit = DO_NOFUNC;
11065 break;
11066 case CMDBUILTIN:
11067 bit = DO_ALTBLTIN;
11068 break;
11069 }
11070 if (act & bit) {
11071 updatetbl = 0;
11072 cmdp = NULL;
11073 } else if (cmdp->rehash == 0)
11074 /* if not invalidated by cd, we're done */
11075 goto success;
11076 }
11077
11078 /* If %builtin not in path, check for builtin next */
11079 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011080 if (bcmd) {
11081 if (IS_BUILTIN_REGULAR(bcmd))
11082 goto builtin_success;
11083 if (act & DO_ALTPATH) {
11084 if (!(act & DO_ALTBLTIN))
11085 goto builtin_success;
11086 } else if (builtinloc <= 0) {
11087 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011088 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011089 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011090
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011091#if ENABLE_FEATURE_SH_STANDALONE
11092 if (find_applet_by_name(name)) {
11093 entry->cmdtype = CMDNORMAL;
11094 entry->u.index = -1;
11095 return;
11096 }
11097#endif
11098
Denis Vlasenkocc571512007-02-23 21:10:35 +000011099 /* We have to search path. */
11100 prev = -1; /* where to start */
11101 if (cmdp && cmdp->rehash) { /* doing a rehash */
11102 if (cmdp->cmdtype == CMDBUILTIN)
11103 prev = builtinloc;
11104 else
11105 prev = cmdp->param.index;
11106 }
11107
11108 e = ENOENT;
11109 idx = -1;
11110 loop:
11111 while ((fullname = padvance(&path, name)) != NULL) {
11112 stunalloc(fullname);
11113 idx++;
11114 if (pathopt) {
11115 if (prefix(pathopt, "builtin")) {
11116 if (bcmd)
11117 goto builtin_success;
11118 continue;
11119 } else if (!(act & DO_NOFUNC) &&
11120 prefix(pathopt, "func")) {
11121 /* handled below */
11122 } else {
11123 /* ignore unimplemented options */
11124 continue;
11125 }
11126 }
11127 /* if rehash, don't redo absolute path names */
11128 if (fullname[0] == '/' && idx <= prev) {
11129 if (idx < prev)
11130 continue;
11131 TRACE(("searchexec \"%s\": no change\n", name));
11132 goto success;
11133 }
11134 while (stat(fullname, &statb) < 0) {
11135#ifdef SYSV
11136 if (errno == EINTR)
11137 continue;
11138#endif
11139 if (errno != ENOENT && errno != ENOTDIR)
11140 e = errno;
11141 goto loop;
11142 }
11143 e = EACCES; /* if we fail, this will be the error */
11144 if (!S_ISREG(statb.st_mode))
11145 continue;
11146 if (pathopt) { /* this is a %func directory */
11147 stalloc(strlen(fullname) + 1);
11148 readcmdfile(fullname);
11149 cmdp = cmdlookup(name, 0);
11150 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11151 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11152 stunalloc(fullname);
11153 goto success;
11154 }
11155 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11156 if (!updatetbl) {
11157 entry->cmdtype = CMDNORMAL;
11158 entry->u.index = idx;
11159 return;
11160 }
11161 INT_OFF;
11162 cmdp = cmdlookup(name, 1);
11163 cmdp->cmdtype = CMDNORMAL;
11164 cmdp->param.index = idx;
11165 INT_ON;
11166 goto success;
11167 }
11168
11169 /* We failed. If there was an entry for this command, delete it */
11170 if (cmdp && updatetbl)
11171 delete_cmd_entry();
11172 if (act & DO_ERR)
11173 ash_msg("%s: %s", name, errmsg(e, "not found"));
11174 entry->cmdtype = CMDUNKNOWN;
11175 return;
11176
11177 builtin_success:
11178 if (!updatetbl) {
11179 entry->cmdtype = CMDBUILTIN;
11180 entry->u.cmd = bcmd;
11181 return;
11182 }
11183 INT_OFF;
11184 cmdp = cmdlookup(name, 1);
11185 cmdp->cmdtype = CMDBUILTIN;
11186 cmdp->param.cmd = bcmd;
11187 INT_ON;
11188 success:
11189 cmdp->rehash = 0;
11190 entry->cmdtype = cmdp->cmdtype;
11191 entry->u = cmdp->param;
11192}
11193
11194
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011195/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011196
Eric Andersencb57d552001-06-28 07:25:16 +000011197/*
Eric Andersencb57d552001-06-28 07:25:16 +000011198 * The trap builtin.
11199 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011200static int
Eric Andersenc470f442003-07-28 09:56:35 +000011201trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011202{
11203 char *action;
11204 char **ap;
11205 int signo;
11206
Eric Andersenc470f442003-07-28 09:56:35 +000011207 nextopt(nullstr);
11208 ap = argptr;
11209 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011210 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011211 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011212 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011213
Rob Landleyc9c1a412006-07-12 19:17:55 +000011214 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011215 out1fmt("trap -- %s %s\n",
11216 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011217 }
11218 }
11219 return 0;
11220 }
Eric Andersenc470f442003-07-28 09:56:35 +000011221 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011222 action = NULL;
11223 else
11224 action = *ap++;
11225 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011226 signo = get_signum(*ap);
11227 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011228 ash_msg_and_raise_error("%s: bad trap", *ap);
11229 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011230 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011231 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011232 action = NULL;
11233 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011234 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011235 }
Eric Andersenc470f442003-07-28 09:56:35 +000011236 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011237 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011238 trap[signo] = action;
11239 if (signo != 0)
11240 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011241 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011242 ap++;
11243 }
11244 return 0;
11245}
11246
Eric Andersenc470f442003-07-28 09:56:35 +000011247
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011248/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011249
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011250#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011251/*
11252 * Lists available builtins
11253 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011254static int
11255helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011256{
11257 int col, i;
11258
11259 out1fmt("\nBuilt-in commands:\n-------------------\n");
11260 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11261 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011262 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011263 if (col > 60) {
11264 out1fmt("\n");
11265 col = 0;
11266 }
11267 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011268#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011269 for (i = 0; i < NUM_APPLETS; i++) {
11270 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11271 if (col > 60) {
11272 out1fmt("\n");
11273 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011274 }
11275 }
11276#endif
11277 out1fmt("\n\n");
11278 return EXIT_SUCCESS;
11279}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011280#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011281
Eric Andersencb57d552001-06-28 07:25:16 +000011282/*
Eric Andersencb57d552001-06-28 07:25:16 +000011283 * The export and readonly commands.
11284 */
Eric Andersenc470f442003-07-28 09:56:35 +000011285static int
11286exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011287{
11288 struct var *vp;
11289 char *name;
11290 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011291 char **aptr;
11292 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011293
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011294 if (nextopt("p") != 'p') {
11295 aptr = argptr;
11296 name = *aptr;
11297 if (name) {
11298 do {
11299 p = strchr(name, '=');
11300 if (p != NULL) {
11301 p++;
11302 } else {
11303 vp = *findvar(hashvar(name), name);
11304 if (vp) {
11305 vp->flags |= flag;
11306 continue;
11307 }
Eric Andersencb57d552001-06-28 07:25:16 +000011308 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011309 setvar(name, p, flag);
11310 } while ((name = *++aptr) != NULL);
11311 return 0;
11312 }
Eric Andersencb57d552001-06-28 07:25:16 +000011313 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011314 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011315 return 0;
11316}
11317
Eric Andersencb57d552001-06-28 07:25:16 +000011318/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011319 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011320 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011321static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011322unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011323{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011324 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011325
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011326 cmdp = cmdlookup(name, 0);
11327 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11328 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011329}
11330
Eric Andersencb57d552001-06-28 07:25:16 +000011331/*
Eric Andersencb57d552001-06-28 07:25:16 +000011332 * The unset builtin command. We unset the function before we unset the
11333 * variable to allow a function to be unset when there is a readonly variable
11334 * with the same name.
11335 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011336static int
Eric Andersenc470f442003-07-28 09:56:35 +000011337unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011338{
11339 char **ap;
11340 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011341 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011342 int ret = 0;
11343
11344 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011345 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011346 }
Eric Andersencb57d552001-06-28 07:25:16 +000011347
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011348 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011349 if (flag != 'f') {
11350 i = unsetvar(*ap);
11351 ret |= i;
11352 if (!(i & 2))
11353 continue;
11354 }
11355 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011356 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011357 }
Eric Andersenc470f442003-07-28 09:56:35 +000011358 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011359}
11360
11361
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011362/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011363
Eric Andersenc470f442003-07-28 09:56:35 +000011364#include <sys/times.h>
11365
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011366static const unsigned char timescmd_str[] = {
11367 ' ', offsetof(struct tms, tms_utime),
11368 '\n', offsetof(struct tms, tms_stime),
11369 ' ', offsetof(struct tms, tms_cutime),
11370 '\n', offsetof(struct tms, tms_cstime),
11371 0
11372};
Eric Andersencb57d552001-06-28 07:25:16 +000011373
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011374static int
11375timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011376{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011377 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011378 const unsigned char *p;
11379 struct tms buf;
11380
11381 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011382 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011383
11384 p = timescmd_str;
11385 do {
11386 t = *(clock_t *)(((char *) &buf) + p[1]);
11387 s = t / clk_tck;
11388 out1fmt("%ldm%ld.%.3lds%c",
11389 s/60, s%60,
11390 ((t - s * clk_tck) * 1000) / clk_tck,
11391 p[0]);
11392 } while (*(p += 2));
11393
Eric Andersencb57d552001-06-28 07:25:16 +000011394 return 0;
11395}
11396
Denis Vlasenko131ae172007-02-18 13:00:19 +000011397#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011398static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011399dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011400{
Eric Andersened9ecf72004-06-22 08:29:45 +000011401 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011402 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011403
Denis Vlasenkob012b102007-02-19 22:43:01 +000011404 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011405 result = arith(s, &errcode);
11406 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011407 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011408 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011409 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011410 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011411 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011412 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011413 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011414 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011415 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011416
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011417 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011418}
Eric Andersenc470f442003-07-28 09:56:35 +000011419
Eric Andersenc470f442003-07-28 09:56:35 +000011420/*
Eric Andersen90898442003-08-06 11:20:52 +000011421 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11422 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11423 *
11424 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011425 */
11426static int
Eric Andersen90898442003-08-06 11:20:52 +000011427letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011428{
Eric Andersenc470f442003-07-28 09:56:35 +000011429 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011430 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011431
Eric Andersen90898442003-08-06 11:20:52 +000011432 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011433 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011434 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011435 for (ap = argv + 1; *ap; ap++) {
11436 i = dash_arith(*ap);
11437 }
Eric Andersenc470f442003-07-28 09:56:35 +000011438
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011439 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011440}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011441#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011442
Eric Andersenc470f442003-07-28 09:56:35 +000011443
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011444/* ============ miscbltin.c
11445 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011446 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011447 */
11448
11449#undef rflag
11450
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011451#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011452typedef enum __rlimit_resource rlim_t;
11453#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011454
Eric Andersenc470f442003-07-28 09:56:35 +000011455/*
11456 * The read builtin. The -e option causes backslashes to escape the
11457 * following character.
11458 *
11459 * This uses unbuffered input, which may be avoidable in some cases.
11460 */
Eric Andersenc470f442003-07-28 09:56:35 +000011461static int
11462readcmd(int argc, char **argv)
11463{
11464 char **ap;
11465 int backslash;
11466 char c;
11467 int rflag;
11468 char *prompt;
11469 const char *ifs;
11470 char *p;
11471 int startword;
11472 int status;
11473 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011474#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011475 int nch_flag = 0;
11476 int nchars = 0;
11477 int silent = 0;
11478 struct termios tty, old_tty;
11479#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011480#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011481 fd_set set;
11482 struct timeval ts;
11483
11484 ts.tv_sec = ts.tv_usec = 0;
11485#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011486
11487 rflag = 0;
11488 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011489#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011490 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011491#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011492 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011493#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011494 while ((i = nextopt("p:rt:")) != '\0')
11495#else
11496 while ((i = nextopt("p:r")) != '\0')
11497#endif
11498 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011499 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011500 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011501 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011502 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011503#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011504 case 'n':
11505 nchars = strtol(optionarg, &p, 10);
11506 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011507 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011508 nch_flag = (nchars > 0);
11509 break;
11510 case 's':
11511 silent = 1;
11512 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011513#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011514#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011515 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011516 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011517 ts.tv_usec = 0;
11518 if (*p == '.') {
11519 char *p2;
11520 if (*++p) {
11521 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011522 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011523 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011524 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011525 scale = p2 - p;
11526 /* normalize to usec */
11527 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011528 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011529 while (scale++ < 6)
11530 ts.tv_usec *= 10;
11531 }
11532 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011533 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011534 }
11535 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011536 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011537 break;
11538#endif
11539 case 'r':
11540 rflag = 1;
11541 break;
11542 default:
11543 break;
11544 }
Eric Andersenc470f442003-07-28 09:56:35 +000011545 }
11546 if (prompt && isatty(0)) {
11547 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011548 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011549 ap = argptr;
11550 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011551 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011552 ifs = bltinlookup("IFS");
11553 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011554 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011555#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011556 if (nch_flag || silent) {
11557 tcgetattr(0, &tty);
11558 old_tty = tty;
11559 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011560 tty.c_lflag &= ~ICANON;
11561 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011562 }
11563 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011564 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011565
11566 }
11567 tcsetattr(0, TCSANOW, &tty);
11568 }
11569#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011570#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011571 if (ts.tv_sec || ts.tv_usec) {
11572 FD_ZERO (&set);
11573 FD_SET (0, &set);
11574
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011575 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011576 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011577#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011578 if (nch_flag)
11579 tcsetattr(0, TCSANOW, &old_tty);
11580#endif
11581 return 1;
11582 }
11583 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011584#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011585 status = 0;
11586 startword = 1;
11587 backslash = 0;
11588 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011589#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011590 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011591#else
11592 for (;;)
11593#endif
11594 {
Eric Andersenc470f442003-07-28 09:56:35 +000011595 if (read(0, &c, 1) != 1) {
11596 status = 1;
11597 break;
11598 }
11599 if (c == '\0')
11600 continue;
11601 if (backslash) {
11602 backslash = 0;
11603 if (c != '\n')
11604 goto put;
11605 continue;
11606 }
11607 if (!rflag && c == '\\') {
11608 backslash++;
11609 continue;
11610 }
11611 if (c == '\n')
11612 break;
11613 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11614 continue;
11615 }
11616 startword = 0;
11617 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11618 STACKSTRNUL(p);
11619 setvar(*ap, stackblock(), 0);
11620 ap++;
11621 startword = 1;
11622 STARTSTACKSTR(p);
11623 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011624 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011625 STPUTC(c, p);
11626 }
11627 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011628#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011629 if (nch_flag || silent)
11630 tcsetattr(0, TCSANOW, &old_tty);
11631#endif
11632
Eric Andersenc470f442003-07-28 09:56:35 +000011633 STACKSTRNUL(p);
11634 /* Remove trailing blanks */
11635 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11636 *p = '\0';
11637 setvar(*ap, stackblock(), 0);
11638 while (*++ap != NULL)
11639 setvar(*ap, nullstr, 0);
11640 return status;
11641}
11642
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011643static int
11644umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011645{
11646 static const char permuser[3] = "ugo";
11647 static const char permmode[3] = "rwx";
11648 static const short int permmask[] = {
11649 S_IRUSR, S_IWUSR, S_IXUSR,
11650 S_IRGRP, S_IWGRP, S_IXGRP,
11651 S_IROTH, S_IWOTH, S_IXOTH
11652 };
11653
11654 char *ap;
11655 mode_t mask;
11656 int i;
11657 int symbolic_mode = 0;
11658
11659 while (nextopt("S") != '\0') {
11660 symbolic_mode = 1;
11661 }
11662
Denis Vlasenkob012b102007-02-19 22:43:01 +000011663 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011664 mask = umask(0);
11665 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011666 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011667
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011668 ap = *argptr;
11669 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011670 if (symbolic_mode) {
11671 char buf[18];
11672 char *p = buf;
11673
11674 for (i = 0; i < 3; i++) {
11675 int j;
11676
11677 *p++ = permuser[i];
11678 *p++ = '=';
11679 for (j = 0; j < 3; j++) {
11680 if ((mask & permmask[3 * i + j]) == 0) {
11681 *p++ = permmode[j];
11682 }
11683 }
11684 *p++ = ',';
11685 }
11686 *--p = 0;
11687 puts(buf);
11688 } else {
11689 out1fmt("%.4o\n", mask);
11690 }
11691 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011692 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011693 mask = 0;
11694 do {
11695 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011696 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011697 mask = (mask << 3) + (*ap - '0');
11698 } while (*++ap != '\0');
11699 umask(mask);
11700 } else {
11701 mask = ~mask & 0777;
11702 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011703 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011704 }
11705 umask(~mask & 0777);
11706 }
11707 }
11708 return 0;
11709}
11710
11711/*
11712 * ulimit builtin
11713 *
11714 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11715 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11716 * ash by J.T. Conklin.
11717 *
11718 * Public domain.
11719 */
11720
11721struct limits {
11722 const char *name;
11723 int cmd;
11724 int factor; /* multiply by to get rlim_{cur,max} values */
11725 char option;
11726};
11727
11728static const struct limits limits[] = {
11729#ifdef RLIMIT_CPU
11730 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11731#endif
11732#ifdef RLIMIT_FSIZE
11733 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11734#endif
11735#ifdef RLIMIT_DATA
11736 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11737#endif
11738#ifdef RLIMIT_STACK
11739 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11740#endif
11741#ifdef RLIMIT_CORE
11742 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11743#endif
11744#ifdef RLIMIT_RSS
11745 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11746#endif
11747#ifdef RLIMIT_MEMLOCK
11748 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11749#endif
11750#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011751 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011752#endif
11753#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011754 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011755#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011756#ifdef RLIMIT_AS
11757 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011758#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011759#ifdef RLIMIT_LOCKS
11760 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011761#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011762 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011763};
11764
Glenn L McGrath76620622004-01-13 10:19:37 +000011765enum limtype { SOFT = 0x1, HARD = 0x2 };
11766
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011767static void
11768printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011769 const struct limits *l)
11770{
11771 rlim_t val;
11772
11773 val = limit->rlim_max;
11774 if (how & SOFT)
11775 val = limit->rlim_cur;
11776
11777 if (val == RLIM_INFINITY)
11778 out1fmt("unlimited\n");
11779 else {
11780 val /= l->factor;
11781 out1fmt("%lld\n", (long long) val);
11782 }
11783}
11784
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011785static int
Eric Andersenc470f442003-07-28 09:56:35 +000011786ulimitcmd(int argc, char **argv)
11787{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011788 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011789 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011790 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011791 const struct limits *l;
11792 int set, all = 0;
11793 int optc, what;
11794 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011795
11796 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011797 while ((optc = nextopt("HSa"
11798#ifdef RLIMIT_CPU
11799 "t"
11800#endif
11801#ifdef RLIMIT_FSIZE
11802 "f"
11803#endif
11804#ifdef RLIMIT_DATA
11805 "d"
11806#endif
11807#ifdef RLIMIT_STACK
11808 "s"
11809#endif
11810#ifdef RLIMIT_CORE
11811 "c"
11812#endif
11813#ifdef RLIMIT_RSS
11814 "m"
11815#endif
11816#ifdef RLIMIT_MEMLOCK
11817 "l"
11818#endif
11819#ifdef RLIMIT_NPROC
11820 "p"
11821#endif
11822#ifdef RLIMIT_NOFILE
11823 "n"
11824#endif
11825#ifdef RLIMIT_AS
11826 "v"
11827#endif
11828#ifdef RLIMIT_LOCKS
11829 "w"
11830#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011831 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011832 switch (optc) {
11833 case 'H':
11834 how = HARD;
11835 break;
11836 case 'S':
11837 how = SOFT;
11838 break;
11839 case 'a':
11840 all = 1;
11841 break;
11842 default:
11843 what = optc;
11844 }
11845
Glenn L McGrath76620622004-01-13 10:19:37 +000011846 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011847 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011848
11849 set = *argptr ? 1 : 0;
11850 if (set) {
11851 char *p = *argptr;
11852
11853 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011854 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011855 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011856 val = RLIM_INFINITY;
11857 else {
11858 val = (rlim_t) 0;
11859
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011860 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011861 val = (val * 10) + (long)(c - '0');
11862 if (val < (rlim_t) 0)
11863 break;
11864 }
11865 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011866 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011867 val *= l->factor;
11868 }
11869 }
11870 if (all) {
11871 for (l = limits; l->name; l++) {
11872 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011873 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011874 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011875 }
11876 return 0;
11877 }
11878
11879 getrlimit(l->cmd, &limit);
11880 if (set) {
11881 if (how & HARD)
11882 limit.rlim_max = val;
11883 if (how & SOFT)
11884 limit.rlim_cur = val;
11885 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011886 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011887 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011888 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011889 }
11890 return 0;
11891}
11892
Eric Andersen90898442003-08-06 11:20:52 +000011893
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011894/* ============ Math support */
11895
Denis Vlasenko131ae172007-02-18 13:00:19 +000011896#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011897
11898/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11899
11900 Permission is hereby granted, free of charge, to any person obtaining
11901 a copy of this software and associated documentation files (the
11902 "Software"), to deal in the Software without restriction, including
11903 without limitation the rights to use, copy, modify, merge, publish,
11904 distribute, sublicense, and/or sell copies of the Software, and to
11905 permit persons to whom the Software is furnished to do so, subject to
11906 the following conditions:
11907
11908 The above copyright notice and this permission notice shall be
11909 included in all copies or substantial portions of the Software.
11910
11911 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11912 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11913 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11914 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11915 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11916 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11917 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11918*/
11919
11920/* This is my infix parser/evaluator. It is optimized for size, intended
11921 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011922 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011923 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000011924 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000011925 * be that which POSIX specifies for shells. */
11926
11927/* The code uses a simple two-stack algorithm. See
11928 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000011929 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000011930 * this is based (this code differs in that it applies operators immediately
11931 * to the stack instead of adding them to a queue to end up with an
11932 * expression). */
11933
11934/* To use the routine, call it with an expression string and error return
11935 * pointer */
11936
11937/*
11938 * Aug 24, 2001 Manuel Novoa III
11939 *
11940 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
11941 *
11942 * 1) In arith_apply():
11943 * a) Cached values of *numptr and &(numptr[-1]).
11944 * b) Removed redundant test for zero denominator.
11945 *
11946 * 2) In arith():
11947 * a) Eliminated redundant code for processing operator tokens by moving
11948 * to a table-based implementation. Also folded handling of parens
11949 * into the table.
11950 * b) Combined all 3 loops which called arith_apply to reduce generated
11951 * code size at the cost of speed.
11952 *
11953 * 3) The following expressions were treated as valid by the original code:
11954 * 1() , 0! , 1 ( *3 ) .
11955 * These bugs have been fixed by internally enclosing the expression in
11956 * parens and then checking that all binary ops and right parens are
11957 * preceded by a valid expression (NUM_TOKEN).
11958 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011959 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000011960 * ctype's isspace() if it is used by another busybox applet or if additional
11961 * whitespace chars should be considered. Look below the "#include"s for a
11962 * precompiler test.
11963 */
11964
11965/*
11966 * Aug 26, 2001 Manuel Novoa III
11967 *
11968 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
11969 *
11970 * Merge in Aaron's comments previously posted to the busybox list,
11971 * modified slightly to take account of my changes to the code.
11972 *
11973 */
11974
11975/*
11976 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
11977 *
11978 * - allow access to variable,
11979 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
11980 * - realize assign syntax (VAR=expr, +=, *= etc)
11981 * - realize exponentiation (** operator)
11982 * - realize comma separated - expr, expr
11983 * - realise ++expr --expr expr++ expr--
11984 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000011985 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000011986 * - was restored loses XOR operator
11987 * - remove one goto label, added three ;-)
11988 * - protect $((num num)) as true zero expr (Manuel`s error)
11989 * - always use special isspace(), see comment from bash ;-)
11990 */
11991
Eric Andersen90898442003-08-06 11:20:52 +000011992#define arith_isspace(arithval) \
11993 (arithval == ' ' || arithval == '\n' || arithval == '\t')
11994
Eric Andersen90898442003-08-06 11:20:52 +000011995typedef unsigned char operator;
11996
11997/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000011998 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000011999 * precedence. The ID portion is so that multiple operators can have the
12000 * same precedence, ensuring that the leftmost one is evaluated first.
12001 * Consider * and /. */
12002
12003#define tok_decl(prec,id) (((id)<<5)|(prec))
12004#define PREC(op) ((op) & 0x1F)
12005
12006#define TOK_LPAREN tok_decl(0,0)
12007
12008#define TOK_COMMA tok_decl(1,0)
12009
12010#define TOK_ASSIGN tok_decl(2,0)
12011#define TOK_AND_ASSIGN tok_decl(2,1)
12012#define TOK_OR_ASSIGN tok_decl(2,2)
12013#define TOK_XOR_ASSIGN tok_decl(2,3)
12014#define TOK_PLUS_ASSIGN tok_decl(2,4)
12015#define TOK_MINUS_ASSIGN tok_decl(2,5)
12016#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12017#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12018
12019#define TOK_MUL_ASSIGN tok_decl(3,0)
12020#define TOK_DIV_ASSIGN tok_decl(3,1)
12021#define TOK_REM_ASSIGN tok_decl(3,2)
12022
12023/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012024#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012025
12026/* conditional is right associativity too */
12027#define TOK_CONDITIONAL tok_decl(4,0)
12028#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12029
12030#define TOK_OR tok_decl(5,0)
12031
12032#define TOK_AND tok_decl(6,0)
12033
12034#define TOK_BOR tok_decl(7,0)
12035
12036#define TOK_BXOR tok_decl(8,0)
12037
12038#define TOK_BAND tok_decl(9,0)
12039
12040#define TOK_EQ tok_decl(10,0)
12041#define TOK_NE tok_decl(10,1)
12042
12043#define TOK_LT tok_decl(11,0)
12044#define TOK_GT tok_decl(11,1)
12045#define TOK_GE tok_decl(11,2)
12046#define TOK_LE tok_decl(11,3)
12047
12048#define TOK_LSHIFT tok_decl(12,0)
12049#define TOK_RSHIFT tok_decl(12,1)
12050
12051#define TOK_ADD tok_decl(13,0)
12052#define TOK_SUB tok_decl(13,1)
12053
12054#define TOK_MUL tok_decl(14,0)
12055#define TOK_DIV tok_decl(14,1)
12056#define TOK_REM tok_decl(14,2)
12057
12058/* exponent is right associativity */
12059#define TOK_EXPONENT tok_decl(15,1)
12060
12061/* For now unary operators. */
12062#define UNARYPREC 16
12063#define TOK_BNOT tok_decl(UNARYPREC,0)
12064#define TOK_NOT tok_decl(UNARYPREC,1)
12065
12066#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12067#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12068
12069#define PREC_PRE (UNARYPREC+2)
12070
12071#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12072#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12073
12074#define PREC_POST (UNARYPREC+3)
12075
12076#define TOK_POST_INC tok_decl(PREC_POST, 0)
12077#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12078
12079#define SPEC_PREC (UNARYPREC+4)
12080
12081#define TOK_NUM tok_decl(SPEC_PREC, 0)
12082#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12083
12084#define NUMPTR (*numstackptr)
12085
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012086static int
12087tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012088{
12089 operator prec = PREC(op);
12090
12091 convert_prec_is_assing(prec);
12092 return (prec == PREC(TOK_ASSIGN) ||
12093 prec == PREC_PRE || prec == PREC_POST);
12094}
12095
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012096static int
12097is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012098{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012099 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12100 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012101}
12102
Eric Andersen90898442003-08-06 11:20:52 +000012103typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012104 arith_t val;
12105 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012106 char contidional_second_val_initialized;
12107 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012108 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012109} v_n_t;
12110
Eric Andersen90898442003-08-06 11:20:52 +000012111typedef struct CHK_VAR_RECURSIVE_LOOPED {
12112 const char *var;
12113 struct CHK_VAR_RECURSIVE_LOOPED *next;
12114} chk_var_recursive_looped_t;
12115
12116static chk_var_recursive_looped_t *prev_chk_var_recursive;
12117
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012118static int
12119arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012120{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012121 if (t->var) {
12122 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012123
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012124 if (p) {
12125 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012126
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012127 /* recursive try as expression */
12128 chk_var_recursive_looped_t *cur;
12129 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012130
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012131 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12132 if (strcmp(cur->var, t->var) == 0) {
12133 /* expression recursion loop detected */
12134 return -5;
12135 }
12136 }
12137 /* save current lookuped var name */
12138 cur = prev_chk_var_recursive;
12139 cur_save.var = t->var;
12140 cur_save.next = cur;
12141 prev_chk_var_recursive = &cur_save;
12142
12143 t->val = arith (p, &errcode);
12144 /* restore previous ptr after recursiving */
12145 prev_chk_var_recursive = cur;
12146 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012147 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012148 /* allow undefined var as 0 */
12149 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012150 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012151 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012152}
12153
12154/* "applying" a token means performing it on the top elements on the integer
12155 * stack. For a unary operator it will only change the top element, but a
12156 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012157static int
12158arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012159{
Eric Andersen90898442003-08-06 11:20:52 +000012160 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012161 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012162 int ret_arith_lookup_val;
12163
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012164 /* There is no operator that can work without arguments */
12165 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012166 numptr_m1 = NUMPTR - 1;
12167
12168 /* check operand is var with noninteger value */
12169 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012170 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012171 return ret_arith_lookup_val;
12172
12173 rez = numptr_m1->val;
12174 if (op == TOK_UMINUS)
12175 rez *= -1;
12176 else if (op == TOK_NOT)
12177 rez = !rez;
12178 else if (op == TOK_BNOT)
12179 rez = ~rez;
12180 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12181 rez++;
12182 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12183 rez--;
12184 else if (op != TOK_UPLUS) {
12185 /* Binary operators */
12186
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012187 /* check and binary operators need two arguments */
12188 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012189
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012190 /* ... and they pop one */
12191 --NUMPTR;
12192 numptr_val = rez;
12193 if (op == TOK_CONDITIONAL) {
12194 if (! numptr_m1->contidional_second_val_initialized) {
12195 /* protect $((expr1 ? expr2)) without ": expr" */
12196 goto err;
12197 }
12198 rez = numptr_m1->contidional_second_val;
12199 } else if (numptr_m1->contidional_second_val_initialized) {
12200 /* protect $((expr1 : expr2)) without "expr ? " */
12201 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012202 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012203 numptr_m1 = NUMPTR - 1;
12204 if (op != TOK_ASSIGN) {
12205 /* check operand is var with noninteger value for not '=' */
12206 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12207 if (ret_arith_lookup_val)
12208 return ret_arith_lookup_val;
12209 }
12210 if (op == TOK_CONDITIONAL) {
12211 numptr_m1->contidional_second_val = rez;
12212 }
12213 rez = numptr_m1->val;
12214 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012215 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012216 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012217 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012218 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012219 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012220 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012221 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012222 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012223 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012224 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012225 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012226 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012227 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012228 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012229 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012230 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012231 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012232 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012233 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012234 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012235 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012236 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012237 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012238 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012239 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012240 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012241 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012242 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012243 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012244 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012245 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012246 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012247 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012248 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012249 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012250 /* protect $((expr : expr)) without "expr ? " */
12251 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012252 }
12253 numptr_m1->contidional_second_val_initialized = op;
12254 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012255 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012256 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012257 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012258 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012259 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012260 return -3; /* exponent less than 0 */
12261 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012262 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012263
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012264 if (numptr_val)
12265 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012266 c *= rez;
12267 rez = c;
12268 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012269 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012270 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012271 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012272 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012273 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012274 rez %= numptr_val;
12275 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012276 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012277 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012278
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012279 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012280 /* Hmm, 1=2 ? */
12281 goto err;
12282 }
12283 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012284#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012285 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012286#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012287 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012288#endif
Eric Andersen90898442003-08-06 11:20:52 +000012289 setvar(numptr_m1->var, buf, 0);
12290 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012291 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012292 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012293 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012294 rez++;
12295 }
12296 numptr_m1->val = rez;
12297 /* protect geting var value, is number now */
12298 numptr_m1->var = NULL;
12299 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012300 err:
12301 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012302}
12303
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012304/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012305static const char op_tokens[] = {
12306 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12307 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12308 '<','<', 0, TOK_LSHIFT,
12309 '>','>', 0, TOK_RSHIFT,
12310 '|','|', 0, TOK_OR,
12311 '&','&', 0, TOK_AND,
12312 '!','=', 0, TOK_NE,
12313 '<','=', 0, TOK_LE,
12314 '>','=', 0, TOK_GE,
12315 '=','=', 0, TOK_EQ,
12316 '|','=', 0, TOK_OR_ASSIGN,
12317 '&','=', 0, TOK_AND_ASSIGN,
12318 '*','=', 0, TOK_MUL_ASSIGN,
12319 '/','=', 0, TOK_DIV_ASSIGN,
12320 '%','=', 0, TOK_REM_ASSIGN,
12321 '+','=', 0, TOK_PLUS_ASSIGN,
12322 '-','=', 0, TOK_MINUS_ASSIGN,
12323 '-','-', 0, TOK_POST_DEC,
12324 '^','=', 0, TOK_XOR_ASSIGN,
12325 '+','+', 0, TOK_POST_INC,
12326 '*','*', 0, TOK_EXPONENT,
12327 '!', 0, TOK_NOT,
12328 '<', 0, TOK_LT,
12329 '>', 0, TOK_GT,
12330 '=', 0, TOK_ASSIGN,
12331 '|', 0, TOK_BOR,
12332 '&', 0, TOK_BAND,
12333 '*', 0, TOK_MUL,
12334 '/', 0, TOK_DIV,
12335 '%', 0, TOK_REM,
12336 '+', 0, TOK_ADD,
12337 '-', 0, TOK_SUB,
12338 '^', 0, TOK_BXOR,
12339 /* uniq */
12340 '~', 0, TOK_BNOT,
12341 ',', 0, TOK_COMMA,
12342 '?', 0, TOK_CONDITIONAL,
12343 ':', 0, TOK_CONDITIONAL_SEP,
12344 ')', 0, TOK_RPAREN,
12345 '(', 0, TOK_LPAREN,
12346 0
12347};
12348/* ptr to ")" */
12349#define endexpression &op_tokens[sizeof(op_tokens)-7]
12350
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012351static arith_t
12352arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012353{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012354 char arithval; /* Current character under analysis */
12355 operator lasttok, op;
12356 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012357
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012358 const char *p = endexpression;
12359 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012360
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012361 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012362
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012363 /* Stack of integers */
12364 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12365 * in any given correct or incorrect expression is left as an exercise to
12366 * the reader. */
12367 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12368 *numstackptr = numstack;
12369 /* Stack of operator tokens */
12370 operator *stack = alloca((datasizes) * sizeof(operator)),
12371 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012372
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012373 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12374 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012375
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012376 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012377 arithval = *expr;
12378 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012379 if (p == endexpression) {
12380 /* Null expression. */
12381 return 0;
12382 }
12383
12384 /* This is only reached after all tokens have been extracted from the
12385 * input stream. If there are still tokens on the operator stack, they
12386 * are to be applied in order. At the end, there should be a final
12387 * result on the integer stack */
12388
12389 if (expr != endexpression + 1) {
12390 /* If we haven't done so already, */
12391 /* append a closing right paren */
12392 expr = endexpression;
12393 /* and let the loop process it. */
12394 continue;
12395 }
12396 /* At this point, we're done with the expression. */
12397 if (numstackptr != numstack+1) {
12398 /* ... but if there isn't, it's bad */
12399 err:
12400 return (*perrcode = -1);
12401 }
12402 if (numstack->var) {
12403 /* expression is $((var)) only, lookup now */
12404 errcode = arith_lookup_val(numstack);
12405 }
12406 ret:
12407 *perrcode = errcode;
12408 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012409 }
12410
Eric Andersen90898442003-08-06 11:20:52 +000012411 /* Continue processing the expression. */
12412 if (arith_isspace(arithval)) {
12413 /* Skip whitespace */
12414 goto prologue;
12415 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012416 p = endofname(expr);
12417 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012418 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012419
12420 numstackptr->var = alloca(var_name_size);
12421 safe_strncpy(numstackptr->var, expr, var_name_size);
12422 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012423 num:
Eric Andersen90898442003-08-06 11:20:52 +000012424 numstackptr->contidional_second_val_initialized = 0;
12425 numstackptr++;
12426 lasttok = TOK_NUM;
12427 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012428 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012429 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012430 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012431#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012432 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012433#else
12434 numstackptr->val = strtol(expr, (char **) &expr, 0);
12435#endif
Eric Andersen90898442003-08-06 11:20:52 +000012436 goto num;
12437 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012438 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012439 const char *o;
12440
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012441 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012442 /* strange operator not found */
12443 goto err;
12444 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012446 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012447 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012448 /* found */
12449 expr = o - 1;
12450 break;
12451 }
12452 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012453 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012454 p++;
12455 /* skip zero delim */
12456 p++;
12457 }
12458 op = p[1];
12459
12460 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012461 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12462 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012463
12464 /* Plus and minus are binary (not unary) _only_ if the last
12465 * token was as number, or a right paren (which pretends to be
12466 * a number, since it evaluates to one). Think about it.
12467 * It makes sense. */
12468 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012469 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012470 case TOK_ADD:
12471 op = TOK_UPLUS;
12472 break;
12473 case TOK_SUB:
12474 op = TOK_UMINUS;
12475 break;
12476 case TOK_POST_INC:
12477 op = TOK_PRE_INC;
12478 break;
12479 case TOK_POST_DEC:
12480 op = TOK_PRE_DEC;
12481 break;
Eric Andersen90898442003-08-06 11:20:52 +000012482 }
12483 }
12484 /* We don't want a unary operator to cause recursive descent on the
12485 * stack, because there can be many in a row and it could cause an
12486 * operator to be evaluated before its argument is pushed onto the
12487 * integer stack. */
12488 /* But for binary operators, "apply" everything on the operator
12489 * stack until we find an operator with a lesser priority than the
12490 * one we have just extracted. */
12491 /* Left paren is given the lowest priority so it will never be
12492 * "applied" in this way.
12493 * if associativity is right and priority eq, applied also skip
12494 */
12495 prec = PREC(op);
12496 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12497 /* not left paren or unary */
12498 if (lasttok != TOK_NUM) {
12499 /* binary op must be preceded by a num */
12500 goto err;
12501 }
12502 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012503 if (op == TOK_RPAREN) {
12504 /* The algorithm employed here is simple: while we don't
12505 * hit an open paren nor the bottom of the stack, pop
12506 * tokens and apply them */
12507 if (stackptr[-1] == TOK_LPAREN) {
12508 --stackptr;
12509 /* Any operator directly after a */
12510 lasttok = TOK_NUM;
12511 /* close paren should consider itself binary */
12512 goto prologue;
12513 }
12514 } else {
12515 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012516
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012517 convert_prec_is_assing(prec);
12518 convert_prec_is_assing(prev_prec);
12519 if (prev_prec < prec)
12520 break;
12521 /* check right assoc */
12522 if (prev_prec == prec && is_right_associativity(prec))
12523 break;
12524 }
12525 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12526 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012527 }
12528 if (op == TOK_RPAREN) {
12529 goto err;
12530 }
12531 }
12532
12533 /* Push this operator to the stack and remember it. */
12534 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012535 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012536 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012537 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012538}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012539#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012540
12541
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012542/* ============ main() and helpers */
12543
12544/*
12545 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012546 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012547static void exitshell(void) ATTRIBUTE_NORETURN;
12548static void
12549exitshell(void)
12550{
12551 struct jmploc loc;
12552 char *p;
12553 int status;
12554
12555 status = exitstatus;
12556 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12557 if (setjmp(loc.loc)) {
12558 if (exception == EXEXIT)
12559/* dash bug: it just does _exit(exitstatus) here
12560 * but we have to do setjobctl(0) first!
12561 * (bug is still not fixed in dash-0.5.3 - if you run dash
12562 * under Midnight Commander, on exit from dash MC is backgrounded) */
12563 status = exitstatus;
12564 goto out;
12565 }
12566 exception_handler = &loc;
12567 p = trap[0];
12568 if (p) {
12569 trap[0] = NULL;
12570 evalstring(p, 0);
12571 }
12572 flush_stdout_stderr();
12573 out:
12574 setjobctl(0);
12575 _exit(status);
12576 /* NOTREACHED */
12577}
12578
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012579static void
12580init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012581{
12582 /* from input.c: */
12583 basepf.nextc = basepf.buf = basebuf;
12584
12585 /* from trap.c: */
12586 signal(SIGCHLD, SIG_DFL);
12587
12588 /* from var.c: */
12589 {
12590 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012591 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012592 const char *p;
12593 struct stat st1, st2;
12594
12595 initvar();
12596 for (envp = environ; envp && *envp; envp++) {
12597 if (strchr(*envp, '=')) {
12598 setvareq(*envp, VEXPORT|VTEXTFIXED);
12599 }
12600 }
12601
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012602 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012603 setvar("PPID", ppid, 0);
12604
12605 p = lookupvar("PWD");
12606 if (p)
12607 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12608 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12609 p = '\0';
12610 setpwd(p, 0);
12611 }
12612}
12613
12614/*
12615 * Process the shell command line arguments.
12616 */
12617static void
12618procargs(int argc, char **argv)
12619{
12620 int i;
12621 const char *xminusc;
12622 char **xargv;
12623
12624 xargv = argv;
12625 arg0 = xargv[0];
12626 if (argc > 0)
12627 xargv++;
12628 for (i = 0; i < NOPTS; i++)
12629 optlist[i] = 2;
12630 argptr = xargv;
12631 options(1);
12632 xargv = argptr;
12633 xminusc = minusc;
12634 if (*xargv == NULL) {
12635 if (xminusc)
12636 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12637 sflag = 1;
12638 }
12639 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12640 iflag = 1;
12641 if (mflag == 2)
12642 mflag = iflag;
12643 for (i = 0; i < NOPTS; i++)
12644 if (optlist[i] == 2)
12645 optlist[i] = 0;
12646#if DEBUG == 2
12647 debug = 1;
12648#endif
12649 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12650 if (xminusc) {
12651 minusc = *xargv++;
12652 if (*xargv)
12653 goto setarg0;
12654 } else if (!sflag) {
12655 setinputfile(*xargv, 0);
12656 setarg0:
12657 arg0 = *xargv++;
12658 commandname = arg0;
12659 }
12660
12661 shellparam.p = xargv;
12662#if ENABLE_ASH_GETOPTS
12663 shellparam.optind = 1;
12664 shellparam.optoff = -1;
12665#endif
12666 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12667 while (*xargv) {
12668 shellparam.nparam++;
12669 xargv++;
12670 }
12671 optschanged();
12672}
12673
12674/*
12675 * Read /etc/profile or .profile.
12676 */
12677static void
12678read_profile(const char *name)
12679{
12680 int skip;
12681
12682 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12683 return;
12684 skip = cmdloop(0);
12685 popfile();
12686 if (skip)
12687 exitshell();
12688}
12689
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012690/*
12691 * This routine is called when an error or an interrupt occurs in an
12692 * interactive shell and control is returned to the main command loop.
12693 */
12694static void
12695reset(void)
12696{
12697 /* from eval.c: */
12698 evalskip = 0;
12699 loopnest = 0;
12700 /* from input.c: */
12701 parselleft = parsenleft = 0; /* clear input buffer */
12702 popallfiles();
12703 /* from parser.c: */
12704 tokpushback = 0;
12705 checkkwd = 0;
12706 /* from redir.c: */
12707 clearredir(0);
12708}
12709
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012710#if PROFILE
12711static short profile_buf[16384];
12712extern int etext();
12713#endif
12714
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012715/*
12716 * Main routine. We initialize things, parse the arguments, execute
12717 * profiles if we're a login shell, and then call cmdloop to execute
12718 * commands. The setjmp call sets up the location to jump to when an
12719 * exception occurs. When an exception occurs the variable "state"
12720 * is used to figure out how far we had gotten.
12721 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012722int ash_main(int argc, char **argv);
12723int ash_main(int argc, char **argv)
12724{
12725 char *shinit;
12726 volatile int state;
12727 struct jmploc jmploc;
12728 struct stackmark smark;
12729
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012730#if PROFILE
12731 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12732#endif
12733
12734#if ENABLE_FEATURE_EDITING
12735 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12736#endif
12737 state = 0;
12738 if (setjmp(jmploc.loc)) {
12739 int e;
12740 int s;
12741
12742 reset();
12743
12744 e = exception;
12745 if (e == EXERROR)
12746 exitstatus = 2;
12747 s = state;
12748 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12749 exitshell();
12750
12751 if (e == EXINT) {
12752 outcslow('\n', stderr);
12753 }
12754 popstackmark(&smark);
12755 FORCE_INT_ON; /* enable interrupts */
12756 if (s == 1)
12757 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012758 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012759 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012760 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012761 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012762 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012763 }
12764 exception_handler = &jmploc;
12765#if DEBUG
12766 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012767 trace_puts("Shell args: ");
12768 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012769#endif
12770 rootpid = getpid();
12771
12772#if ENABLE_ASH_RANDOM_SUPPORT
12773 rseed = rootpid + time(NULL);
12774#endif
12775 init();
12776 setstackmark(&smark);
12777 procargs(argc, argv);
12778#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12779 if (iflag) {
12780 const char *hp = lookupvar("HISTFILE");
12781
12782 if (hp == NULL) {
12783 hp = lookupvar("HOME");
12784 if (hp != NULL) {
12785 char *defhp = concat_path_file(hp, ".ash_history");
12786 setvar("HISTFILE", defhp, 0);
12787 free(defhp);
12788 }
12789 }
12790 }
12791#endif
12792 if (argv[0] && argv[0][0] == '-')
12793 isloginsh = 1;
12794 if (isloginsh) {
12795 state = 1;
12796 read_profile("/etc/profile");
12797 state1:
12798 state = 2;
12799 read_profile(".profile");
12800 }
12801 state2:
12802 state = 3;
12803 if (
12804#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012805 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012806#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012807 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012808 ) {
12809 shinit = lookupvar("ENV");
12810 if (shinit != NULL && *shinit != '\0') {
12811 read_profile(shinit);
12812 }
12813 }
12814 state3:
12815 state = 4;
12816 if (minusc)
12817 evalstring(minusc, 0);
12818
12819 if (sflag || minusc == NULL) {
12820#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12821 if ( iflag ) {
12822 const char *hp = lookupvar("HISTFILE");
12823
12824 if (hp != NULL)
12825 line_input_state->hist_file = hp;
12826 }
12827#endif
12828 state4: /* XXX ??? - why isn't this before the "if" statement */
12829 cmdloop(1);
12830 }
12831#if PROFILE
12832 monitor(0);
12833#endif
12834#ifdef GPROF
12835 {
12836 extern void _mcleanup(void);
12837 _mcleanup();
12838 }
12839#endif
12840 exitshell();
12841 /* NOTREACHED */
12842}
12843
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012844#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012845const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012846int main(int argc, char **argv)
12847{
12848 return ash_main(argc, argv);
12849}
12850#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012851
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012852
Eric Andersendf82f612001-06-28 07:46:40 +000012853/*-
12854 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012855 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012856 *
12857 * This code is derived from software contributed to Berkeley by
12858 * Kenneth Almquist.
12859 *
12860 * Redistribution and use in source and binary forms, with or without
12861 * modification, are permitted provided that the following conditions
12862 * are met:
12863 * 1. Redistributions of source code must retain the above copyright
12864 * notice, this list of conditions and the following disclaimer.
12865 * 2. Redistributions in binary form must reproduce the above copyright
12866 * notice, this list of conditions and the following disclaimer in the
12867 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012868 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012869 * may be used to endorse or promote products derived from this software
12870 * without specific prior written permission.
12871 *
12872 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12873 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12874 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12875 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12876 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12877 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12878 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12879 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12880 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12881 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12882 * SUCH DAMAGE.
12883 */