blob: 35eec42a7efb4d526e99dce60123ce8e9122394f [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 +00001584#ifdef IFS_BROKEN
1585static const char defifsvar[] = "IFS= \t\n";
1586#define defifs (defifsvar + 4)
1587#else
1588static const char defifs[] = " \t\n";
1589#endif
1590
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001591struct shparam {
1592 int nparam; /* # of positional parameters (without $0) */
1593 unsigned char malloc; /* if parameter list dynamically allocated */
1594 char **p; /* parameter list */
1595#if ENABLE_ASH_GETOPTS
1596 int optind; /* next parameter to be processed by getopts */
1597 int optoff; /* used by getopts */
1598#endif
1599};
1600
1601static struct shparam shellparam; /* $@ current positional parameters */
1602
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00001603/*
1604 * Free the list of positional parameters.
1605 */
1606static void
1607freeparam(volatile struct shparam *param)
1608{
1609 char **ap;
1610
1611 if (param->malloc) {
1612 for (ap = param->p; *ap; ap++)
1613 free(*ap);
1614 free(param->p);
1615 }
1616}
1617
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001618#if ENABLE_ASH_GETOPTS
1619static void
1620getoptsreset(const char *value)
1621{
1622 shellparam.optind = number(value);
1623 shellparam.optoff = -1;
1624}
1625#endif
1626
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001627struct var {
1628 struct var *next; /* next entry in hash list */
1629 int flags; /* flags are defined above */
1630 const char *text; /* name=value */
1631 void (*func)(const char *); /* function to be called when */
1632 /* the variable gets set/unset */
1633};
1634
1635struct localvar {
1636 struct localvar *next; /* next local variable in list */
1637 struct var *vp; /* the variable that was made local */
1638 int flags; /* saved flags */
1639 const char *text; /* saved text */
1640};
1641
1642/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001643#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001644static void
1645change_lc_all(const char *value)
1646{
1647 if (value && *value != '\0')
1648 setlocale(LC_ALL, value);
1649}
1650static void
1651change_lc_ctype(const char *value)
1652{
1653 if (value && *value != '\0')
1654 setlocale(LC_CTYPE, value);
1655}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001656#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001657#if ENABLE_ASH_MAIL
1658static void chkmail(void);
1659static void changemail(const char *);
1660#endif
1661static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001662#if ENABLE_ASH_RANDOM_SUPPORT
1663static void change_random(const char *);
1664#endif
1665
1666static struct var varinit[] = {
1667#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001668 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001669#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001670 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001671#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001672#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001673 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1674 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001675#endif
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00001676 { NULL, VSTRFIXED|VTEXTFIXED, bb_PATH_root_path, changepath },
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001677 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1678 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1679 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001680#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001681 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001682#endif
1683#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001684 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001685#endif
1686#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001687 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1688 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001689#endif
1690#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001691 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001692#endif
1693};
1694
1695#define vifs varinit[0]
1696#if ENABLE_ASH_MAIL
1697#define vmail (&vifs)[1]
1698#define vmpath (&vmail)[1]
1699#else
1700#define vmpath vifs
1701#endif
1702#define vpath (&vmpath)[1]
1703#define vps1 (&vpath)[1]
1704#define vps2 (&vps1)[1]
1705#define vps4 (&vps2)[1]
1706#define voptind (&vps4)[1]
1707#if ENABLE_ASH_GETOPTS
1708#define vrandom (&voptind)[1]
1709#else
1710#define vrandom (&vps4)[1]
1711#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001712
1713/*
1714 * The following macros access the values of the above variables.
1715 * They have to skip over the name. They return the null string
1716 * for unset variables.
1717 */
1718#define ifsval() (vifs.text + 4)
1719#define ifsset() ((vifs.flags & VUNSET) == 0)
1720#define mailval() (vmail.text + 5)
1721#define mpathval() (vmpath.text + 9)
1722#define pathval() (vpath.text + 5)
1723#define ps1val() (vps1.text + 4)
1724#define ps2val() (vps2.text + 4)
1725#define ps4val() (vps4.text + 4)
1726#define optindval() (voptind.text + 7)
1727
1728#define mpathset() ((vmpath.flags & VUNSET) == 0)
1729
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001730/*
1731 * The parsefile structure pointed to by the global variable parsefile
1732 * contains information about the current file being read.
1733 */
1734struct redirtab {
1735 struct redirtab *next;
1736 int renamed[10];
1737 int nullredirs;
1738};
1739
1740static struct redirtab *redirlist;
1741static int nullredirs;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1743
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#define VTABSIZE 39
1745
1746static struct var *vartab[VTABSIZE];
1747
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1749#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1750
1751/*
1752 * Return of a legal variable name (a letter or underscore followed by zero or
1753 * more letters, underscores, and digits).
1754 */
1755static char *
1756endofname(const char *name)
1757{
1758 char *p;
1759
1760 p = (char *) name;
1761 if (!is_name(*p))
1762 return p;
1763 while (*++p) {
1764 if (!is_in_name(*p))
1765 break;
1766 }
1767 return p;
1768}
1769
1770/*
1771 * Compares two strings up to the first = or '\0'. The first
1772 * string must be terminated by '='; the second may be terminated by
1773 * either '=' or '\0'.
1774 */
1775static int
1776varcmp(const char *p, const char *q)
1777{
1778 int c, d;
1779
1780 while ((c = *p) == (d = *q)) {
1781 if (!c || c == '=')
1782 goto out;
1783 p++;
1784 q++;
1785 }
1786 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001787 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001788 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001789 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790 out:
1791 return c - d;
1792}
1793
1794static int
1795varequal(const char *a, const char *b)
1796{
1797 return !varcmp(a, b);
1798}
1799
1800/*
1801 * Find the appropriate entry in the hash table from the name.
1802 */
1803static struct var **
1804hashvar(const char *p)
1805{
1806 unsigned hashval;
1807
1808 hashval = ((unsigned char) *p) << 4;
1809 while (*p && *p != '=')
1810 hashval += (unsigned char) *p++;
1811 return &vartab[hashval % VTABSIZE];
1812}
1813
1814static int
1815vpcmp(const void *a, const void *b)
1816{
1817 return varcmp(*(const char **)a, *(const char **)b);
1818}
1819
1820/*
1821 * This routine initializes the builtin variables.
1822 */
1823static void
1824initvar(void)
1825{
1826 struct var *vp;
1827 struct var *end;
1828 struct var **vpp;
1829
1830 /*
1831 * PS1 depends on uid
1832 */
1833#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1834 vps1.text = "PS1=\\w \\$ ";
1835#else
1836 if (!geteuid())
1837 vps1.text = "PS1=# ";
1838#endif
1839 vp = varinit;
1840 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1841 do {
1842 vpp = hashvar(vp->text);
1843 vp->next = *vpp;
1844 *vpp = vp;
1845 } while (++vp < end);
1846}
1847
1848static struct var **
1849findvar(struct var **vpp, const char *name)
1850{
1851 for (; *vpp; vpp = &(*vpp)->next) {
1852 if (varequal((*vpp)->text, name)) {
1853 break;
1854 }
1855 }
1856 return vpp;
1857}
1858
1859/*
1860 * Find the value of a variable. Returns NULL if not set.
1861 */
1862static char *
1863lookupvar(const char *name)
1864{
1865 struct var *v;
1866
1867 v = *findvar(hashvar(name), name);
1868 if (v) {
1869#ifdef DYNAMIC_VAR
1870 /*
1871 * Dynamic variables are implemented roughly the same way they are
1872 * in bash. Namely, they're "special" so long as they aren't unset.
1873 * As soon as they're unset, they're no longer dynamic, and dynamic
1874 * lookup will no longer happen at that point. -- PFM.
1875 */
1876 if ((v->flags & VDYNAMIC))
1877 (*v->func)(NULL);
1878#endif
1879 if (!(v->flags & VUNSET))
1880 return strchrnul(v->text, '=') + 1;
1881 }
1882 return NULL;
1883}
1884
1885/*
1886 * Search the environment of a builtin command.
1887 */
1888static char *
1889bltinlookup(const char *name)
1890{
1891 struct strlist *sp;
1892
1893 for (sp = cmdenviron; sp; sp = sp->next) {
1894 if (varequal(sp->text, name))
1895 return strchrnul(sp->text, '=') + 1;
1896 }
1897 return lookupvar(name);
1898}
1899
1900/*
1901 * Same as setvar except that the variable and value are passed in
1902 * the first argument as name=value. Since the first argument will
1903 * be actually stored in the table, it should not be a string that
1904 * will go away.
1905 * Called with interrupts off.
1906 */
1907static void
1908setvareq(char *s, int flags)
1909{
1910 struct var *vp, **vpp;
1911
1912 vpp = hashvar(s);
1913 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1914 vp = *findvar(vpp, s);
1915 if (vp) {
1916 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1917 const char *n;
1918
1919 if (flags & VNOSAVE)
1920 free(s);
1921 n = vp->text;
1922 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1923 }
1924
1925 if (flags & VNOSET)
1926 return;
1927
1928 if (vp->func && (flags & VNOFUNC) == 0)
1929 (*vp->func)(strchrnul(s, '=') + 1);
1930
1931 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1932 free((char*)vp->text);
1933
1934 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1935 } else {
1936 if (flags & VNOSET)
1937 return;
1938 /* not found */
1939 vp = ckmalloc(sizeof(*vp));
1940 vp->next = *vpp;
1941 vp->func = NULL;
1942 *vpp = vp;
1943 }
1944 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1945 s = ckstrdup(s);
1946 vp->text = s;
1947 vp->flags = flags;
1948}
1949
1950/*
1951 * Set the value of a variable. The flags argument is ored with the
1952 * flags of the variable. If val is NULL, the variable is unset.
1953 */
1954static void
1955setvar(const char *name, const char *val, int flags)
1956{
1957 char *p, *q;
1958 size_t namelen;
1959 char *nameeq;
1960 size_t vallen;
1961
1962 q = endofname(name);
1963 p = strchrnul(q, '=');
1964 namelen = p - name;
1965 if (!namelen || p != q)
1966 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1967 vallen = 0;
1968 if (val == NULL) {
1969 flags |= VUNSET;
1970 } else {
1971 vallen = strlen(val);
1972 }
1973 INT_OFF;
1974 nameeq = ckmalloc(namelen + vallen + 2);
1975 p = memcpy(nameeq, name, namelen) + namelen;
1976 if (val) {
1977 *p++ = '=';
1978 p = memcpy(p, val, vallen) + vallen;
1979 }
1980 *p = '\0';
1981 setvareq(nameeq, flags | VNOSAVE);
1982 INT_ON;
1983}
1984
1985#if ENABLE_ASH_GETOPTS
1986/*
1987 * Safe version of setvar, returns 1 on success 0 on failure.
1988 */
1989static int
1990setvarsafe(const char *name, const char *val, int flags)
1991{
1992 int err;
1993 volatile int saveint;
1994 struct jmploc *volatile savehandler = exception_handler;
1995 struct jmploc jmploc;
1996
1997 SAVE_INT(saveint);
1998 if (setjmp(jmploc.loc))
1999 err = 1;
2000 else {
2001 exception_handler = &jmploc;
2002 setvar(name, val, flags);
2003 err = 0;
2004 }
2005 exception_handler = savehandler;
2006 RESTORE_INT(saveint);
2007 return err;
2008}
2009#endif
2010
2011/*
2012 * Unset the specified variable.
2013 */
2014static int
2015unsetvar(const char *s)
2016{
2017 struct var **vpp;
2018 struct var *vp;
2019 int retval;
2020
2021 vpp = findvar(hashvar(s), s);
2022 vp = *vpp;
2023 retval = 2;
2024 if (vp) {
2025 int flags = vp->flags;
2026
2027 retval = 1;
2028 if (flags & VREADONLY)
2029 goto out;
2030#ifdef DYNAMIC_VAR
2031 vp->flags &= ~VDYNAMIC;
2032#endif
2033 if (flags & VUNSET)
2034 goto ok;
2035 if ((flags & VSTRFIXED) == 0) {
2036 INT_OFF;
2037 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2038 free((char*)vp->text);
2039 *vpp = vp->next;
2040 free(vp);
2041 INT_ON;
2042 } else {
2043 setvar(s, 0, 0);
2044 vp->flags &= ~VEXPORT;
2045 }
2046 ok:
2047 retval = 0;
2048 }
2049 out:
2050 return retval;
2051}
2052
2053/*
2054 * Process a linked list of variable assignments.
2055 */
2056static void
2057listsetvar(struct strlist *list_set_var, int flags)
2058{
2059 struct strlist *lp = list_set_var;
2060
2061 if (!lp)
2062 return;
2063 INT_OFF;
2064 do {
2065 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002066 lp = lp->next;
2067 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002068 INT_ON;
2069}
2070
2071/*
2072 * Generate a list of variables satisfying the given conditions.
2073 */
2074static char **
2075listvars(int on, int off, char ***end)
2076{
2077 struct var **vpp;
2078 struct var *vp;
2079 char **ep;
2080 int mask;
2081
2082 STARTSTACKSTR(ep);
2083 vpp = vartab;
2084 mask = on | off;
2085 do {
2086 for (vp = *vpp; vp; vp = vp->next) {
2087 if ((vp->flags & mask) == on) {
2088 if (ep == stackstrend())
2089 ep = growstackstr();
2090 *ep++ = (char *) vp->text;
2091 }
2092 }
2093 } while (++vpp < vartab + VTABSIZE);
2094 if (ep == stackstrend())
2095 ep = growstackstr();
2096 if (end)
2097 *end = ep;
2098 *ep++ = NULL;
2099 return grabstackstr(ep);
2100}
2101
2102
2103/* ============ Path search helper
2104 *
2105 * The variable path (passed by reference) should be set to the start
2106 * of the path before the first call; padvance will update
2107 * this value as it proceeds. Successive calls to padvance will return
2108 * the possible path expansions in sequence. If an option (indicated by
2109 * a percent sign) appears in the path entry then the global variable
2110 * pathopt will be set to point to it; otherwise pathopt will be set to
2111 * NULL.
2112 */
2113static const char *pathopt; /* set by padvance */
2114
2115static char *
2116padvance(const char **path, const char *name)
2117{
2118 const char *p;
2119 char *q;
2120 const char *start;
2121 size_t len;
2122
2123 if (*path == NULL)
2124 return NULL;
2125 start = *path;
2126 for (p = start; *p && *p != ':' && *p != '%'; p++);
2127 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2128 while (stackblocksize() < len)
2129 growstackblock();
2130 q = stackblock();
2131 if (p != start) {
2132 memcpy(q, start, p - start);
2133 q += p - start;
2134 *q++ = '/';
2135 }
2136 strcpy(q, name);
2137 pathopt = NULL;
2138 if (*p == '%') {
2139 pathopt = ++p;
2140 while (*p && *p != ':') p++;
2141 }
2142 if (*p == ':')
2143 *path = p + 1;
2144 else
2145 *path = NULL;
2146 return stalloc(len);
2147}
2148
2149
2150/* ============ Prompt */
2151
2152static int doprompt; /* if set, prompt the user */
2153static int needprompt; /* true if interactive and at start of line */
2154
2155#if ENABLE_FEATURE_EDITING
2156static line_input_t *line_input_state;
2157static const char *cmdedit_prompt;
2158static void
2159putprompt(const char *s)
2160{
2161 if (ENABLE_ASH_EXPAND_PRMT) {
2162 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002163 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002164 return;
2165 }
2166 cmdedit_prompt = s;
2167}
2168#else
2169static void
2170putprompt(const char *s)
2171{
2172 out2str(s);
2173}
2174#endif
2175
2176#if ENABLE_ASH_EXPAND_PRMT
2177/* expandstr() needs parsing machinery, so it is far away ahead... */
2178static const char *expandstr(const char *ps);
2179#else
2180#define expandstr(s) s
2181#endif
2182
2183static void
2184setprompt(int whichprompt)
2185{
2186 const char *prompt;
2187#if ENABLE_ASH_EXPAND_PRMT
2188 struct stackmark smark;
2189#endif
2190
2191 needprompt = 0;
2192
2193 switch (whichprompt) {
2194 case 1:
2195 prompt = ps1val();
2196 break;
2197 case 2:
2198 prompt = ps2val();
2199 break;
2200 default: /* 0 */
2201 prompt = nullstr;
2202 }
2203#if ENABLE_ASH_EXPAND_PRMT
2204 setstackmark(&smark);
2205 stalloc(stackblocksize());
2206#endif
2207 putprompt(expandstr(prompt));
2208#if ENABLE_ASH_EXPAND_PRMT
2209 popstackmark(&smark);
2210#endif
2211}
2212
2213
2214/* ============ The cd and pwd commands */
2215
2216#define CD_PHYSICAL 1
2217#define CD_PRINT 2
2218
2219static int docd(const char *, int);
2220
2221static char *curdir = nullstr; /* current working directory */
2222static char *physdir = nullstr; /* physical working directory */
2223
2224static int
2225cdopt(void)
2226{
2227 int flags = 0;
2228 int i, j;
2229
2230 j = 'L';
2231 while ((i = nextopt("LP"))) {
2232 if (i != j) {
2233 flags ^= CD_PHYSICAL;
2234 j = i;
2235 }
2236 }
2237
2238 return flags;
2239}
2240
2241/*
2242 * Update curdir (the name of the current directory) in response to a
2243 * cd command.
2244 */
2245static const char *
2246updatepwd(const char *dir)
2247{
2248 char *new;
2249 char *p;
2250 char *cdcomppath;
2251 const char *lim;
2252
2253 cdcomppath = ststrdup(dir);
2254 STARTSTACKSTR(new);
2255 if (*dir != '/') {
2256 if (curdir == nullstr)
2257 return 0;
2258 new = stack_putstr(curdir, new);
2259 }
2260 new = makestrspace(strlen(dir) + 2, new);
2261 lim = stackblock() + 1;
2262 if (*dir != '/') {
2263 if (new[-1] != '/')
2264 USTPUTC('/', new);
2265 if (new > lim && *lim == '/')
2266 lim++;
2267 } else {
2268 USTPUTC('/', new);
2269 cdcomppath++;
2270 if (dir[1] == '/' && dir[2] != '/') {
2271 USTPUTC('/', new);
2272 cdcomppath++;
2273 lim++;
2274 }
2275 }
2276 p = strtok(cdcomppath, "/");
2277 while (p) {
2278 switch (*p) {
2279 case '.':
2280 if (p[1] == '.' && p[2] == '\0') {
2281 while (new > lim) {
2282 STUNPUTC(new);
2283 if (new[-1] == '/')
2284 break;
2285 }
2286 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002287 }
2288 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002289 break;
2290 /* fall through */
2291 default:
2292 new = stack_putstr(p, new);
2293 USTPUTC('/', new);
2294 }
2295 p = strtok(0, "/");
2296 }
2297 if (new > lim)
2298 STUNPUTC(new);
2299 *new = 0;
2300 return stackblock();
2301}
2302
2303/*
2304 * Find out what the current directory is. If we already know the current
2305 * directory, this routine returns immediately.
2306 */
2307static char *
2308getpwd(void)
2309{
2310 char *dir = getcwd(0, 0);
2311 return dir ? dir : nullstr;
2312}
2313
2314static void
2315setpwd(const char *val, int setold)
2316{
2317 char *oldcur, *dir;
2318
2319 oldcur = dir = curdir;
2320
2321 if (setold) {
2322 setvar("OLDPWD", oldcur, VEXPORT);
2323 }
2324 INT_OFF;
2325 if (physdir != nullstr) {
2326 if (physdir != oldcur)
2327 free(physdir);
2328 physdir = nullstr;
2329 }
2330 if (oldcur == val || !val) {
2331 char *s = getpwd();
2332 physdir = s;
2333 if (!val)
2334 dir = s;
2335 } else
2336 dir = ckstrdup(val);
2337 if (oldcur != dir && oldcur != nullstr) {
2338 free(oldcur);
2339 }
2340 curdir = dir;
2341 INT_ON;
2342 setvar("PWD", dir, VEXPORT);
2343}
2344
2345static void hashcd(void);
2346
2347/*
2348 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2349 * know that the current directory has changed.
2350 */
2351static int
2352docd(const char *dest, int flags)
2353{
2354 const char *dir = 0;
2355 int err;
2356
2357 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2358
2359 INT_OFF;
2360 if (!(flags & CD_PHYSICAL)) {
2361 dir = updatepwd(dest);
2362 if (dir)
2363 dest = dir;
2364 }
2365 err = chdir(dest);
2366 if (err)
2367 goto out;
2368 setpwd(dir, 1);
2369 hashcd();
2370 out:
2371 INT_ON;
2372 return err;
2373}
2374
2375static int
2376cdcmd(int argc, char **argv)
2377{
2378 const char *dest;
2379 const char *path;
2380 const char *p;
2381 char c;
2382 struct stat statb;
2383 int flags;
2384
2385 flags = cdopt();
2386 dest = *argptr;
2387 if (!dest)
2388 dest = bltinlookup(homestr);
2389 else if (LONE_DASH(dest)) {
2390 dest = bltinlookup("OLDPWD");
2391 flags |= CD_PRINT;
2392 }
2393 if (!dest)
2394 dest = nullstr;
2395 if (*dest == '/')
2396 goto step7;
2397 if (*dest == '.') {
2398 c = dest[1];
2399 dotdot:
2400 switch (c) {
2401 case '\0':
2402 case '/':
2403 goto step6;
2404 case '.':
2405 c = dest[2];
2406 if (c != '.')
2407 goto dotdot;
2408 }
2409 }
2410 if (!*dest)
2411 dest = ".";
2412 path = bltinlookup("CDPATH");
2413 if (!path) {
2414 step6:
2415 step7:
2416 p = dest;
2417 goto docd;
2418 }
2419 do {
2420 c = *path;
2421 p = padvance(&path, dest);
2422 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2423 if (c && c != ':')
2424 flags |= CD_PRINT;
2425 docd:
2426 if (!docd(p, flags))
2427 goto out;
2428 break;
2429 }
2430 } while (path);
2431 ash_msg_and_raise_error("can't cd to %s", dest);
2432 /* NOTREACHED */
2433 out:
2434 if (flags & CD_PRINT)
2435 out1fmt(snlfmt, curdir);
2436 return 0;
2437}
2438
2439static int
2440pwdcmd(int argc, char **argv)
2441{
2442 int flags;
2443 const char *dir = curdir;
2444
2445 flags = cdopt();
2446 if (flags) {
2447 if (physdir == nullstr)
2448 setpwd(dir, 0);
2449 dir = physdir;
2450 }
2451 out1fmt(snlfmt, dir);
2452 return 0;
2453}
2454
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002455
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002456/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002457
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002458#define IBUFSIZ (BUFSIZ + 1)
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002459#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002460
Eric Andersenc470f442003-07-28 09:56:35 +00002461/* Syntax classes */
2462#define CWORD 0 /* character is nothing special */
2463#define CNL 1 /* newline character */
2464#define CBACK 2 /* a backslash character */
2465#define CSQUOTE 3 /* single quote */
2466#define CDQUOTE 4 /* double quote */
2467#define CENDQUOTE 5 /* a terminating quote */
2468#define CBQUOTE 6 /* backwards single quote */
2469#define CVAR 7 /* a dollar sign */
2470#define CENDVAR 8 /* a '}' character */
2471#define CLP 9 /* a left paren in arithmetic */
2472#define CRP 10 /* a right paren in arithmetic */
2473#define CENDFILE 11 /* end of file */
2474#define CCTL 12 /* like CWORD, except it must be escaped */
2475#define CSPCL 13 /* these terminate a word */
2476#define CIGN 14 /* character should be ignored */
2477
Denis Vlasenko131ae172007-02-18 13:00:19 +00002478#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002479#define SYNBASE 130
2480#define PEOF -130
2481#define PEOA -129
2482#define PEOA_OR_PEOF PEOA
2483#else
2484#define SYNBASE 129
2485#define PEOF -129
2486#define PEOA_OR_PEOF PEOF
2487#endif
2488
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002489/* number syntax index */
2490#define BASESYNTAX 0 /* not in quotes */
2491#define DQSYNTAX 1 /* in double quotes */
2492#define SQSYNTAX 2 /* in single quotes */
2493#define ARISYNTAX 3 /* in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002494
Denis Vlasenko131ae172007-02-18 13:00:19 +00002495#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002496#define USE_SIT_FUNCTION
2497#endif
2498
Denis Vlasenko131ae172007-02-18 13:00:19 +00002499#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002500static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002501#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002502 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002503#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002504 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2505 { CNL, CNL, CNL, CNL }, /* 2, \n */
2506 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2507 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2508 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2509 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2510 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2511 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2512 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2513 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2514 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002515#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002516 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2517 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2518 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002519#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002520};
Eric Andersenc470f442003-07-28 09:56:35 +00002521#else
2522static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002523#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002524 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002525#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002526 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2527 { CNL, CNL, CNL }, /* 2, \n */
2528 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2529 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2530 { CVAR, CVAR, CWORD }, /* 5, $ */
2531 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2532 { CSPCL, CWORD, CWORD }, /* 7, ( */
2533 { CSPCL, CWORD, CWORD }, /* 8, ) */
2534 { CBACK, CBACK, CCTL }, /* 9, \ */
2535 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2536 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002537#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002538 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2539 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2540 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002541#endif
2542};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002543#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002544
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002545#ifdef USE_SIT_FUNCTION
2546
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002547static int
2548SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002549{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002550 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002551#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002552 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002553 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2554 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2555 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2556 11, 3 /* "}~" */
2557 };
2558#else
2559 static const char syntax_index_table[] = {
2560 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2561 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2562 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2563 10, 2 /* "}~" */
2564 };
2565#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002566 const char *s;
2567 int indx;
2568
Eric Andersenc470f442003-07-28 09:56:35 +00002569 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002570 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002571#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002572 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002573 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002574 else
2575#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002576#define U_C(c) ((unsigned char)(c))
2577
2578 if ((unsigned char)c >= (unsigned char)(CTLESC)
2579 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2580 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002581 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002582 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002583 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002584 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002585 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002586 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002587 }
2588 return S_I_T[indx][syntax];
2589}
2590
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002591#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002592
Denis Vlasenko131ae172007-02-18 13:00:19 +00002593#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002594#define CSPCL_CIGN_CIGN_CIGN 0
2595#define CSPCL_CWORD_CWORD_CWORD 1
2596#define CNL_CNL_CNL_CNL 2
2597#define CWORD_CCTL_CCTL_CWORD 3
2598#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2599#define CVAR_CVAR_CWORD_CVAR 5
2600#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2601#define CSPCL_CWORD_CWORD_CLP 7
2602#define CSPCL_CWORD_CWORD_CRP 8
2603#define CBACK_CBACK_CCTL_CBACK 9
2604#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2605#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2606#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2607#define CWORD_CWORD_CWORD_CWORD 13
2608#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002609#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002610#define CSPCL_CWORD_CWORD_CWORD 0
2611#define CNL_CNL_CNL_CNL 1
2612#define CWORD_CCTL_CCTL_CWORD 2
2613#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2614#define CVAR_CVAR_CWORD_CVAR 4
2615#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2616#define CSPCL_CWORD_CWORD_CLP 6
2617#define CSPCL_CWORD_CWORD_CRP 7
2618#define CBACK_CBACK_CCTL_CBACK 8
2619#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2620#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2621#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2622#define CWORD_CWORD_CWORD_CWORD 12
2623#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002624#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002625
2626static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002627 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002628 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002629#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002630 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2631#endif
2632 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2633 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2634 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2635 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2636 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2637 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2638 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2639 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2640 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002641 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2642 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2643 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2644 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2645 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2646 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2647 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2648 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2649 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2650 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2770 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2771 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2793 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002794 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002795 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2796 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2797 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2798 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002799 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002800 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2801 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2802 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2803 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2804 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2805 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2806 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2807 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2808 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2809 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2810 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2811 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2812 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2813 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2814 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2815 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2816 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2817 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2818 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2819 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2820 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2821 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2822 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2823 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2824 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2825 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2826 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2827 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2828 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2829 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2830 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2831 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2832 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2833 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2851 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2852 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2853 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2854 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2855 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2856 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2857 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2858 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2859 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2860 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2861 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2862 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2863 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2865 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2866 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2885 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2886 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2887 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002888};
2889
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002890#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2891
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002892#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002893
Eric Andersen2870d962001-07-02 17:27:21 +00002894
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002895/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002896
Denis Vlasenko131ae172007-02-18 13:00:19 +00002897#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002898
2899#define ALIASINUSE 1
2900#define ALIASDEAD 2
2901
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002902#define ATABSIZE 39
2903
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002904struct alias {
2905 struct alias *next;
2906 char *name;
2907 char *val;
2908 int flag;
2909};
2910
Eric Andersen2870d962001-07-02 17:27:21 +00002911static struct alias *atab[ATABSIZE];
2912
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002913static struct alias **
2914__lookupalias(const char *name) {
2915 unsigned int hashval;
2916 struct alias **app;
2917 const char *p;
2918 unsigned int ch;
2919
2920 p = name;
2921
2922 ch = (unsigned char)*p;
2923 hashval = ch << 4;
2924 while (ch) {
2925 hashval += ch;
2926 ch = (unsigned char)*++p;
2927 }
2928 app = &atab[hashval % ATABSIZE];
2929
2930 for (; *app; app = &(*app)->next) {
2931 if (strcmp(name, (*app)->name) == 0) {
2932 break;
2933 }
2934 }
2935
2936 return app;
2937}
2938
2939static struct alias *
2940lookupalias(const char *name, int check)
2941{
2942 struct alias *ap = *__lookupalias(name);
2943
2944 if (check && ap && (ap->flag & ALIASINUSE))
2945 return NULL;
2946 return ap;
2947}
2948
2949static struct alias *
2950freealias(struct alias *ap)
2951{
2952 struct alias *next;
2953
2954 if (ap->flag & ALIASINUSE) {
2955 ap->flag |= ALIASDEAD;
2956 return ap;
2957 }
2958
2959 next = ap->next;
2960 free(ap->name);
2961 free(ap->val);
2962 free(ap);
2963 return next;
2964}
Eric Andersencb57d552001-06-28 07:25:16 +00002965
Eric Andersenc470f442003-07-28 09:56:35 +00002966static void
2967setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002968{
2969 struct alias *ap, **app;
2970
2971 app = __lookupalias(name);
2972 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002973 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002974 if (ap) {
2975 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002976 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002977 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002978 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002979 ap->flag &= ~ALIASDEAD;
2980 } else {
2981 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002982 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002983 ap->name = ckstrdup(name);
2984 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002985 ap->flag = 0;
2986 ap->next = 0;
2987 *app = ap;
2988 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002989 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002990}
2991
Eric Andersenc470f442003-07-28 09:56:35 +00002992static int
2993unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00002994{
Eric Andersencb57d552001-06-28 07:25:16 +00002995 struct alias **app;
2996
2997 app = __lookupalias(name);
2998
2999 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003000 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003001 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003002 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003003 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003004 }
3005
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003006 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003007}
3008
Eric Andersenc470f442003-07-28 09:56:35 +00003009static void
3010rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003011{
Eric Andersencb57d552001-06-28 07:25:16 +00003012 struct alias *ap, **app;
3013 int i;
3014
Denis Vlasenkob012b102007-02-19 22:43:01 +00003015 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003016 for (i = 0; i < ATABSIZE; i++) {
3017 app = &atab[i];
3018 for (ap = *app; ap; ap = *app) {
3019 *app = freealias(*app);
3020 if (ap == *app) {
3021 app = &ap->next;
3022 }
3023 }
3024 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003025 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003026}
3027
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003028static void
3029printalias(const struct alias *ap)
3030{
3031 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3032}
3033
Eric Andersencb57d552001-06-28 07:25:16 +00003034/*
3035 * TODO - sort output
3036 */
Eric Andersenc470f442003-07-28 09:56:35 +00003037static int
3038aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003039{
3040 char *n, *v;
3041 int ret = 0;
3042 struct alias *ap;
3043
3044 if (argc == 1) {
3045 int i;
3046
3047 for (i = 0; i < ATABSIZE; i++)
3048 for (ap = atab[i]; ap; ap = ap->next) {
3049 printalias(ap);
3050 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003051 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003052 }
3053 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003054 v = strchr(n+1, '=');
3055 if (v == NULL) { /* n+1: funny ksh stuff */
3056 ap = *__lookupalias(n);
3057 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003058 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003059 ret = 1;
3060 } else
3061 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003062 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003063 *v++ = '\0';
3064 setalias(n, v);
3065 }
3066 }
3067
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003068 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003069}
3070
Eric Andersenc470f442003-07-28 09:56:35 +00003071static int
3072unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003073{
3074 int i;
3075
3076 while ((i = nextopt("a")) != '\0') {
3077 if (i == 'a') {
3078 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003079 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003080 }
3081 }
3082 for (i = 0; *argptr; argptr++) {
3083 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003084 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003085 i = 1;
3086 }
3087 }
3088
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003089 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003090}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003091
Denis Vlasenko131ae172007-02-18 13:00:19 +00003092#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003093
Eric Andersenc470f442003-07-28 09:56:35 +00003094
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003095/* ============ jobs.c */
3096
3097/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3098#define FORK_FG 0
3099#define FORK_BG 1
3100#define FORK_NOJOB 2
3101
3102/* mode flags for showjob(s) */
3103#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3104#define SHOW_PID 0x04 /* include process pid */
3105#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3106
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003107/*
3108 * A job structure contains information about a job. A job is either a
3109 * single process or a set of processes contained in a pipeline. In the
3110 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3111 * array of pids.
3112 */
3113
3114struct procstat {
3115 pid_t pid; /* process id */
3116 int status; /* last process status from wait() */
3117 char *cmd; /* text of command being run */
3118};
3119
3120struct job {
3121 struct procstat ps0; /* status of process */
3122 struct procstat *ps; /* status or processes when more than one */
3123#if JOBS
3124 int stopstatus; /* status of a stopped job */
3125#endif
3126 uint32_t
3127 nprocs: 16, /* number of processes */
3128 state: 8,
3129#define JOBRUNNING 0 /* at least one proc running */
3130#define JOBSTOPPED 1 /* all procs are stopped */
3131#define JOBDONE 2 /* all procs are completed */
3132#if JOBS
3133 sigint: 1, /* job was killed by SIGINT */
3134 jobctl: 1, /* job running under job control */
3135#endif
3136 waited: 1, /* true if this entry has been waited for */
3137 used: 1, /* true if this entry is in used */
3138 changed: 1; /* true if status has changed */
3139 struct job *prev_job; /* previous job */
3140};
3141
3142static pid_t backgndpid; /* pid of last background process */
3143static int job_warning; /* user was warned about stopped jobs */
3144#if JOBS
3145static int jobctl; /* true if doing job control */
3146#endif
3147
3148static struct job *makejob(union node *, int);
3149static int forkshell(struct job *, union node *, int);
3150static int waitforjob(struct job *);
3151
3152#if ! JOBS
3153#define setjobctl(on) /* do nothing */
3154#else
3155static void setjobctl(int);
3156static void showjobs(FILE *, int);
3157#endif
3158
3159/*
3160 * Set the signal handler for the specified signal. The routine figures
3161 * out what it should be set to.
3162 */
3163static void
3164setsignal(int signo)
3165{
3166 int action;
3167 char *t, tsig;
3168 struct sigaction act;
3169
3170 t = trap[signo];
3171 if (t == NULL)
3172 action = S_DFL;
3173 else if (*t != '\0')
3174 action = S_CATCH;
3175 else
3176 action = S_IGN;
3177 if (rootshell && action == S_DFL) {
3178 switch (signo) {
3179 case SIGINT:
3180 if (iflag || minusc || sflag == 0)
3181 action = S_CATCH;
3182 break;
3183 case SIGQUIT:
3184#if DEBUG
3185 if (debug)
3186 break;
3187#endif
3188 /* FALLTHROUGH */
3189 case SIGTERM:
3190 if (iflag)
3191 action = S_IGN;
3192 break;
3193#if JOBS
3194 case SIGTSTP:
3195 case SIGTTOU:
3196 if (mflag)
3197 action = S_IGN;
3198 break;
3199#endif
3200 }
3201 }
3202
3203 t = &sigmode[signo - 1];
3204 tsig = *t;
3205 if (tsig == 0) {
3206 /*
3207 * current setting unknown
3208 */
3209 if (sigaction(signo, 0, &act) == -1) {
3210 /*
3211 * Pretend it worked; maybe we should give a warning
3212 * here, but other shells don't. We don't alter
3213 * sigmode, so that we retry every time.
3214 */
3215 return;
3216 }
3217 if (act.sa_handler == SIG_IGN) {
3218 if (mflag
3219 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3220 ) {
3221 tsig = S_IGN; /* don't hard ignore these */
3222 } else
3223 tsig = S_HARD_IGN;
3224 } else {
3225 tsig = S_RESET; /* force to be set */
3226 }
3227 }
3228 if (tsig == S_HARD_IGN || tsig == action)
3229 return;
3230 switch (action) {
3231 case S_CATCH:
3232 act.sa_handler = onsig;
3233 break;
3234 case S_IGN:
3235 act.sa_handler = SIG_IGN;
3236 break;
3237 default:
3238 act.sa_handler = SIG_DFL;
3239 }
3240 *t = action;
3241 act.sa_flags = 0;
3242 sigfillset(&act.sa_mask);
3243 sigaction(signo, &act, 0);
3244}
3245
3246/* mode flags for set_curjob */
3247#define CUR_DELETE 2
3248#define CUR_RUNNING 1
3249#define CUR_STOPPED 0
3250
3251/* mode flags for dowait */
3252#define DOWAIT_NORMAL 0
3253#define DOWAIT_BLOCK 1
3254
3255#if JOBS
3256/* pgrp of shell on invocation */
3257static int initialpgrp;
3258static int ttyfd = -1;
3259#endif
3260/* array of jobs */
3261static struct job *jobtab;
3262/* size of array */
3263static unsigned njobs;
3264/* current job */
3265static struct job *curjob;
3266/* number of presumed living untracked jobs */
3267static int jobless;
3268
3269static void
3270set_curjob(struct job *jp, unsigned mode)
3271{
3272 struct job *jp1;
3273 struct job **jpp, **curp;
3274
3275 /* first remove from list */
3276 jpp = curp = &curjob;
3277 do {
3278 jp1 = *jpp;
3279 if (jp1 == jp)
3280 break;
3281 jpp = &jp1->prev_job;
3282 } while (1);
3283 *jpp = jp1->prev_job;
3284
3285 /* Then re-insert in correct position */
3286 jpp = curp;
3287 switch (mode) {
3288 default:
3289#if DEBUG
3290 abort();
3291#endif
3292 case CUR_DELETE:
3293 /* job being deleted */
3294 break;
3295 case CUR_RUNNING:
3296 /* newly created job or backgrounded job,
3297 put after all stopped jobs. */
3298 do {
3299 jp1 = *jpp;
3300#if JOBS
3301 if (!jp1 || jp1->state != JOBSTOPPED)
3302#endif
3303 break;
3304 jpp = &jp1->prev_job;
3305 } while (1);
3306 /* FALLTHROUGH */
3307#if JOBS
3308 case CUR_STOPPED:
3309#endif
3310 /* newly stopped job - becomes curjob */
3311 jp->prev_job = *jpp;
3312 *jpp = jp;
3313 break;
3314 }
3315}
3316
3317#if JOBS || DEBUG
3318static int
3319jobno(const struct job *jp)
3320{
3321 return jp - jobtab + 1;
3322}
3323#endif
3324
3325/*
3326 * Convert a job name to a job structure.
3327 */
3328static struct job *
3329getjob(const char *name, int getctl)
3330{
3331 struct job *jp;
3332 struct job *found;
3333 const char *err_msg = "No such job: %s";
3334 unsigned num;
3335 int c;
3336 const char *p;
3337 char *(*match)(const char *, const char *);
3338
3339 jp = curjob;
3340 p = name;
3341 if (!p)
3342 goto currentjob;
3343
3344 if (*p != '%')
3345 goto err;
3346
3347 c = *++p;
3348 if (!c)
3349 goto currentjob;
3350
3351 if (!p[1]) {
3352 if (c == '+' || c == '%') {
3353 currentjob:
3354 err_msg = "No current job";
3355 goto check;
3356 }
3357 if (c == '-') {
3358 if (jp)
3359 jp = jp->prev_job;
3360 err_msg = "No previous job";
3361 check:
3362 if (!jp)
3363 goto err;
3364 goto gotit;
3365 }
3366 }
3367
3368 if (is_number(p)) {
3369 num = atoi(p);
3370 if (num < njobs) {
3371 jp = jobtab + num - 1;
3372 if (jp->used)
3373 goto gotit;
3374 goto err;
3375 }
3376 }
3377
3378 match = prefix;
3379 if (*p == '?') {
3380 match = strstr;
3381 p++;
3382 }
3383
3384 found = 0;
3385 while (1) {
3386 if (!jp)
3387 goto err;
3388 if (match(jp->ps[0].cmd, p)) {
3389 if (found)
3390 goto err;
3391 found = jp;
3392 err_msg = "%s: ambiguous";
3393 }
3394 jp = jp->prev_job;
3395 }
3396
3397 gotit:
3398#if JOBS
3399 err_msg = "job %s not created under job control";
3400 if (getctl && jp->jobctl == 0)
3401 goto err;
3402#endif
3403 return jp;
3404 err:
3405 ash_msg_and_raise_error(err_msg, name);
3406}
3407
3408/*
3409 * Mark a job structure as unused.
3410 */
3411static void
3412freejob(struct job *jp)
3413{
3414 struct procstat *ps;
3415 int i;
3416
3417 INT_OFF;
3418 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3419 if (ps->cmd != nullstr)
3420 free(ps->cmd);
3421 }
3422 if (jp->ps != &jp->ps0)
3423 free(jp->ps);
3424 jp->used = 0;
3425 set_curjob(jp, CUR_DELETE);
3426 INT_ON;
3427}
3428
3429#if JOBS
3430static void
3431xtcsetpgrp(int fd, pid_t pgrp)
3432{
3433 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003434 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003435}
3436
3437/*
3438 * Turn job control on and off.
3439 *
3440 * Note: This code assumes that the third arg to ioctl is a character
3441 * pointer, which is true on Berkeley systems but not System V. Since
3442 * System V doesn't have job control yet, this isn't a problem now.
3443 *
3444 * Called with interrupts off.
3445 */
3446static void
3447setjobctl(int on)
3448{
3449 int fd;
3450 int pgrp;
3451
3452 if (on == jobctl || rootshell == 0)
3453 return;
3454 if (on) {
3455 int ofd;
3456 ofd = fd = open(_PATH_TTY, O_RDWR);
3457 if (fd < 0) {
3458 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3459 * That sometimes helps to acquire controlling tty.
3460 * Obviously, a workaround for bugs when someone
3461 * failed to provide a controlling tty to bash! :) */
3462 fd += 3;
3463 while (!isatty(fd) && --fd >= 0)
3464 ;
3465 }
3466 fd = fcntl(fd, F_DUPFD, 10);
3467 close(ofd);
3468 if (fd < 0)
3469 goto out;
3470 fcntl(fd, F_SETFD, FD_CLOEXEC);
3471 do { /* while we are in the background */
3472 pgrp = tcgetpgrp(fd);
3473 if (pgrp < 0) {
3474 out:
3475 ash_msg("can't access tty; job control turned off");
3476 mflag = on = 0;
3477 goto close;
3478 }
3479 if (pgrp == getpgrp())
3480 break;
3481 killpg(0, SIGTTIN);
3482 } while (1);
3483 initialpgrp = pgrp;
3484
3485 setsignal(SIGTSTP);
3486 setsignal(SIGTTOU);
3487 setsignal(SIGTTIN);
3488 pgrp = rootpid;
3489 setpgid(0, pgrp);
3490 xtcsetpgrp(fd, pgrp);
3491 } else {
3492 /* turning job control off */
3493 fd = ttyfd;
3494 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003495 /* was xtcsetpgrp, but this can make exiting ash
3496 * with pty already deleted loop forever */
3497 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003498 setpgid(0, pgrp);
3499 setsignal(SIGTSTP);
3500 setsignal(SIGTTOU);
3501 setsignal(SIGTTIN);
3502 close:
3503 close(fd);
3504 fd = -1;
3505 }
3506 ttyfd = fd;
3507 jobctl = on;
3508}
3509
3510static int
3511killcmd(int argc, char **argv)
3512{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003513 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3514 int i = 1;
3515 do {
3516 if (argv[i][0] == '%') {
3517 struct job *jp = getjob(argv[i], 0);
3518 unsigned pid = jp->ps[0].pid;
3519 /* Enough space for ' -NNN<nul>' */
3520 argv[i] = alloca(sizeof(int)*3 + 3);
3521 /* kill_main has matching code to expect
3522 * leading space. Needed to not confuse
3523 * negative pids with "kill -SIGNAL_NO" syntax */
3524 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003525 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003526 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003527 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003528 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003529}
3530
3531static void
3532showpipe(struct job *jp, FILE *out)
3533{
3534 struct procstat *sp;
3535 struct procstat *spend;
3536
3537 spend = jp->ps + jp->nprocs;
3538 for (sp = jp->ps + 1; sp < spend; sp++)
3539 fprintf(out, " | %s", sp->cmd);
3540 outcslow('\n', out);
3541 flush_stdout_stderr();
3542}
3543
3544
3545static int
3546restartjob(struct job *jp, int mode)
3547{
3548 struct procstat *ps;
3549 int i;
3550 int status;
3551 pid_t pgid;
3552
3553 INT_OFF;
3554 if (jp->state == JOBDONE)
3555 goto out;
3556 jp->state = JOBRUNNING;
3557 pgid = jp->ps->pid;
3558 if (mode == FORK_FG)
3559 xtcsetpgrp(ttyfd, pgid);
3560 killpg(pgid, SIGCONT);
3561 ps = jp->ps;
3562 i = jp->nprocs;
3563 do {
3564 if (WIFSTOPPED(ps->status)) {
3565 ps->status = -1;
3566 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003567 ps++;
3568 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003569 out:
3570 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3571 INT_ON;
3572 return status;
3573}
3574
3575static int
3576fg_bgcmd(int argc, char **argv)
3577{
3578 struct job *jp;
3579 FILE *out;
3580 int mode;
3581 int retval;
3582
3583 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3584 nextopt(nullstr);
3585 argv = argptr;
3586 out = stdout;
3587 do {
3588 jp = getjob(*argv, 1);
3589 if (mode == FORK_BG) {
3590 set_curjob(jp, CUR_RUNNING);
3591 fprintf(out, "[%d] ", jobno(jp));
3592 }
3593 outstr(jp->ps->cmd, out);
3594 showpipe(jp, out);
3595 retval = restartjob(jp, mode);
3596 } while (*argv && *++argv);
3597 return retval;
3598}
3599#endif
3600
3601static int
3602sprint_status(char *s, int status, int sigonly)
3603{
3604 int col;
3605 int st;
3606
3607 col = 0;
3608 if (!WIFEXITED(status)) {
3609#if JOBS
3610 if (WIFSTOPPED(status))
3611 st = WSTOPSIG(status);
3612 else
3613#endif
3614 st = WTERMSIG(status);
3615 if (sigonly) {
3616 if (st == SIGINT || st == SIGPIPE)
3617 goto out;
3618#if JOBS
3619 if (WIFSTOPPED(status))
3620 goto out;
3621#endif
3622 }
3623 st &= 0x7f;
3624 col = fmtstr(s, 32, strsignal(st));
3625 if (WCOREDUMP(status)) {
3626 col += fmtstr(s + col, 16, " (core dumped)");
3627 }
3628 } else if (!sigonly) {
3629 st = WEXITSTATUS(status);
3630 if (st)
3631 col = fmtstr(s, 16, "Done(%d)", st);
3632 else
3633 col = fmtstr(s, 16, "Done");
3634 }
3635 out:
3636 return col;
3637}
3638
3639/*
3640 * Do a wait system call. If job control is compiled in, we accept
3641 * stopped processes. If block is zero, we return a value of zero
3642 * rather than blocking.
3643 *
3644 * System V doesn't have a non-blocking wait system call. It does
3645 * have a SIGCLD signal that is sent to a process when one of it's
3646 * children dies. The obvious way to use SIGCLD would be to install
3647 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3648 * was received, and have waitproc bump another counter when it got
3649 * the status of a process. Waitproc would then know that a wait
3650 * system call would not block if the two counters were different.
3651 * This approach doesn't work because if a process has children that
3652 * have not been waited for, System V will send it a SIGCLD when it
3653 * installs a signal handler for SIGCLD. What this means is that when
3654 * a child exits, the shell will be sent SIGCLD signals continuously
3655 * until is runs out of stack space, unless it does a wait call before
3656 * restoring the signal handler. The code below takes advantage of
3657 * this (mis)feature by installing a signal handler for SIGCLD and
3658 * then checking to see whether it was called. If there are any
3659 * children to be waited for, it will be.
3660 *
3661 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3662 * waits at all. In this case, the user will not be informed when
3663 * a background process until the next time she runs a real program
3664 * (as opposed to running a builtin command or just typing return),
3665 * and the jobs command may give out of date information.
3666 */
3667static int
3668waitproc(int block, int *status)
3669{
3670 int flags = 0;
3671
3672#if JOBS
3673 if (jobctl)
3674 flags |= WUNTRACED;
3675#endif
3676 if (block == 0)
3677 flags |= WNOHANG;
3678 return wait3(status, flags, (struct rusage *)NULL);
3679}
3680
3681/*
3682 * Wait for a process to terminate.
3683 */
3684static int
3685dowait(int block, struct job *job)
3686{
3687 int pid;
3688 int status;
3689 struct job *jp;
3690 struct job *thisjob;
3691 int state;
3692
3693 TRACE(("dowait(%d) called\n", block));
3694 pid = waitproc(block, &status);
3695 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3696 if (pid <= 0)
3697 return pid;
3698 INT_OFF;
3699 thisjob = NULL;
3700 for (jp = curjob; jp; jp = jp->prev_job) {
3701 struct procstat *sp;
3702 struct procstat *spend;
3703 if (jp->state == JOBDONE)
3704 continue;
3705 state = JOBDONE;
3706 spend = jp->ps + jp->nprocs;
3707 sp = jp->ps;
3708 do {
3709 if (sp->pid == pid) {
3710 TRACE(("Job %d: changing status of proc %d "
3711 "from 0x%x to 0x%x\n",
3712 jobno(jp), pid, sp->status, status));
3713 sp->status = status;
3714 thisjob = jp;
3715 }
3716 if (sp->status == -1)
3717 state = JOBRUNNING;
3718#if JOBS
3719 if (state == JOBRUNNING)
3720 continue;
3721 if (WIFSTOPPED(sp->status)) {
3722 jp->stopstatus = sp->status;
3723 state = JOBSTOPPED;
3724 }
3725#endif
3726 } while (++sp < spend);
3727 if (thisjob)
3728 goto gotjob;
3729 }
3730#if JOBS
3731 if (!WIFSTOPPED(status))
3732#endif
3733
3734 jobless--;
3735 goto out;
3736
3737 gotjob:
3738 if (state != JOBRUNNING) {
3739 thisjob->changed = 1;
3740
3741 if (thisjob->state != state) {
3742 TRACE(("Job %d: changing state from %d to %d\n",
3743 jobno(thisjob), thisjob->state, state));
3744 thisjob->state = state;
3745#if JOBS
3746 if (state == JOBSTOPPED) {
3747 set_curjob(thisjob, CUR_STOPPED);
3748 }
3749#endif
3750 }
3751 }
3752
3753 out:
3754 INT_ON;
3755
3756 if (thisjob && thisjob == job) {
3757 char s[48 + 1];
3758 int len;
3759
3760 len = sprint_status(s, status, 1);
3761 if (len) {
3762 s[len] = '\n';
3763 s[len + 1] = 0;
3764 out2str(s);
3765 }
3766 }
3767 return pid;
3768}
3769
3770#if JOBS
3771static void
3772showjob(FILE *out, struct job *jp, int mode)
3773{
3774 struct procstat *ps;
3775 struct procstat *psend;
3776 int col;
3777 int indent;
3778 char s[80];
3779
3780 ps = jp->ps;
3781
3782 if (mode & SHOW_PGID) {
3783 /* just output process (group) id of pipeline */
3784 fprintf(out, "%d\n", ps->pid);
3785 return;
3786 }
3787
3788 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3789 indent = col;
3790
3791 if (jp == curjob)
3792 s[col - 2] = '+';
3793 else if (curjob && jp == curjob->prev_job)
3794 s[col - 2] = '-';
3795
3796 if (mode & SHOW_PID)
3797 col += fmtstr(s + col, 16, "%d ", ps->pid);
3798
3799 psend = ps + jp->nprocs;
3800
3801 if (jp->state == JOBRUNNING) {
3802 strcpy(s + col, "Running");
3803 col += sizeof("Running") - 1;
3804 } else {
3805 int status = psend[-1].status;
3806 if (jp->state == JOBSTOPPED)
3807 status = jp->stopstatus;
3808 col += sprint_status(s + col, status, 0);
3809 }
3810
3811 goto start;
3812
3813 do {
3814 /* for each process */
3815 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
3816 start:
3817 fprintf(out, "%s%*c%s",
3818 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3819 );
3820 if (!(mode & SHOW_PID)) {
3821 showpipe(jp, out);
3822 break;
3823 }
3824 if (++ps == psend) {
3825 outcslow('\n', out);
3826 break;
3827 }
3828 } while (1);
3829
3830 jp->changed = 0;
3831
3832 if (jp->state == JOBDONE) {
3833 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3834 freejob(jp);
3835 }
3836}
3837
3838static int
3839jobscmd(int argc, char **argv)
3840{
3841 int mode, m;
3842 FILE *out;
3843
3844 mode = 0;
3845 while ((m = nextopt("lp"))) {
3846 if (m == 'l')
3847 mode = SHOW_PID;
3848 else
3849 mode = SHOW_PGID;
3850 }
3851
3852 out = stdout;
3853 argv = argptr;
3854 if (*argv) {
3855 do
3856 showjob(out, getjob(*argv,0), mode);
3857 while (*++argv);
3858 } else
3859 showjobs(out, mode);
3860
3861 return 0;
3862}
3863
3864/*
3865 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3866 * statuses have changed since the last call to showjobs.
3867 */
3868static void
3869showjobs(FILE *out, int mode)
3870{
3871 struct job *jp;
3872
3873 TRACE(("showjobs(%x) called\n", mode));
3874
3875 /* If not even one one job changed, there is nothing to do */
3876 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3877 continue;
3878
3879 for (jp = curjob; jp; jp = jp->prev_job) {
3880 if (!(mode & SHOW_CHANGED) || jp->changed)
3881 showjob(out, jp, mode);
3882 }
3883}
3884#endif /* JOBS */
3885
3886static int
3887getstatus(struct job *job)
3888{
3889 int status;
3890 int retval;
3891
3892 status = job->ps[job->nprocs - 1].status;
3893 retval = WEXITSTATUS(status);
3894 if (!WIFEXITED(status)) {
3895#if JOBS
3896 retval = WSTOPSIG(status);
3897 if (!WIFSTOPPED(status))
3898#endif
3899 {
3900 /* XXX: limits number of signals */
3901 retval = WTERMSIG(status);
3902#if JOBS
3903 if (retval == SIGINT)
3904 job->sigint = 1;
3905#endif
3906 }
3907 retval += 128;
3908 }
3909 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3910 jobno(job), job->nprocs, status, retval));
3911 return retval;
3912}
3913
3914static int
3915waitcmd(int argc, char **argv)
3916{
3917 struct job *job;
3918 int retval;
3919 struct job *jp;
3920
3921 EXSIGON;
3922
3923 nextopt(nullstr);
3924 retval = 0;
3925
3926 argv = argptr;
3927 if (!*argv) {
3928 /* wait for all jobs */
3929 for (;;) {
3930 jp = curjob;
3931 while (1) {
3932 if (!jp) {
3933 /* no running procs */
3934 goto out;
3935 }
3936 if (jp->state == JOBRUNNING)
3937 break;
3938 jp->waited = 1;
3939 jp = jp->prev_job;
3940 }
3941 dowait(DOWAIT_BLOCK, 0);
3942 }
3943 }
3944
3945 retval = 127;
3946 do {
3947 if (**argv != '%') {
3948 pid_t pid = number(*argv);
3949 job = curjob;
3950 goto start;
3951 do {
3952 if (job->ps[job->nprocs - 1].pid == pid)
3953 break;
3954 job = job->prev_job;
3955 start:
3956 if (!job)
3957 goto repeat;
3958 } while (1);
3959 } else
3960 job = getjob(*argv, 0);
3961 /* loop until process terminated or stopped */
3962 while (job->state == JOBRUNNING)
3963 dowait(DOWAIT_BLOCK, 0);
3964 job->waited = 1;
3965 retval = getstatus(job);
3966 repeat:
3967 ;
3968 } while (*++argv);
3969
3970 out:
3971 return retval;
3972}
3973
3974static struct job *
3975growjobtab(void)
3976{
3977 size_t len;
3978 ptrdiff_t offset;
3979 struct job *jp, *jq;
3980
3981 len = njobs * sizeof(*jp);
3982 jq = jobtab;
3983 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
3984
3985 offset = (char *)jp - (char *)jq;
3986 if (offset) {
3987 /* Relocate pointers */
3988 size_t l = len;
3989
3990 jq = (struct job *)((char *)jq + l);
3991 while (l) {
3992 l -= sizeof(*jp);
3993 jq--;
3994#define joff(p) ((struct job *)((char *)(p) + l))
3995#define jmove(p) (p) = (void *)((char *)(p) + offset)
3996 if (joff(jp)->ps == &jq->ps0)
3997 jmove(joff(jp)->ps);
3998 if (joff(jp)->prev_job)
3999 jmove(joff(jp)->prev_job);
4000 }
4001 if (curjob)
4002 jmove(curjob);
4003#undef joff
4004#undef jmove
4005 }
4006
4007 njobs += 4;
4008 jobtab = jp;
4009 jp = (struct job *)((char *)jp + len);
4010 jq = jp + 3;
4011 do {
4012 jq->used = 0;
4013 } while (--jq >= jp);
4014 return jp;
4015}
4016
4017/*
4018 * Return a new job structure.
4019 * Called with interrupts off.
4020 */
4021static struct job *
4022makejob(union node *node, int nprocs)
4023{
4024 int i;
4025 struct job *jp;
4026
4027 for (i = njobs, jp = jobtab; ; jp++) {
4028 if (--i < 0) {
4029 jp = growjobtab();
4030 break;
4031 }
4032 if (jp->used == 0)
4033 break;
4034 if (jp->state != JOBDONE || !jp->waited)
4035 continue;
4036#if JOBS
4037 if (jobctl)
4038 continue;
4039#endif
4040 freejob(jp);
4041 break;
4042 }
4043 memset(jp, 0, sizeof(*jp));
4044#if JOBS
4045 if (jobctl)
4046 jp->jobctl = 1;
4047#endif
4048 jp->prev_job = curjob;
4049 curjob = jp;
4050 jp->used = 1;
4051 jp->ps = &jp->ps0;
4052 if (nprocs > 1) {
4053 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4054 }
4055 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4056 jobno(jp)));
4057 return jp;
4058}
4059
4060#if JOBS
4061/*
4062 * Return a string identifying a command (to be printed by the
4063 * jobs command).
4064 */
4065static char *cmdnextc;
4066
4067static void
4068cmdputs(const char *s)
4069{
4070 const char *p, *str;
4071 char c, cc[2] = " ";
4072 char *nextc;
4073 int subtype = 0;
4074 int quoted = 0;
4075 static const char vstype[VSTYPE + 1][4] = {
4076 "", "}", "-", "+", "?", "=",
4077 "%", "%%", "#", "##"
4078 };
4079
4080 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4081 p = s;
4082 while ((c = *p++) != 0) {
4083 str = 0;
4084 switch (c) {
4085 case CTLESC:
4086 c = *p++;
4087 break;
4088 case CTLVAR:
4089 subtype = *p++;
4090 if ((subtype & VSTYPE) == VSLENGTH)
4091 str = "${#";
4092 else
4093 str = "${";
4094 if (!(subtype & VSQUOTE) == !(quoted & 1))
4095 goto dostr;
4096 quoted ^= 1;
4097 c = '"';
4098 break;
4099 case CTLENDVAR:
4100 str = "\"}" + !(quoted & 1);
4101 quoted >>= 1;
4102 subtype = 0;
4103 goto dostr;
4104 case CTLBACKQ:
4105 str = "$(...)";
4106 goto dostr;
4107 case CTLBACKQ+CTLQUOTE:
4108 str = "\"$(...)\"";
4109 goto dostr;
4110#if ENABLE_ASH_MATH_SUPPORT
4111 case CTLARI:
4112 str = "$((";
4113 goto dostr;
4114 case CTLENDARI:
4115 str = "))";
4116 goto dostr;
4117#endif
4118 case CTLQUOTEMARK:
4119 quoted ^= 1;
4120 c = '"';
4121 break;
4122 case '=':
4123 if (subtype == 0)
4124 break;
4125 if ((subtype & VSTYPE) != VSNORMAL)
4126 quoted <<= 1;
4127 str = vstype[subtype & VSTYPE];
4128 if (subtype & VSNUL)
4129 c = ':';
4130 else
4131 goto checkstr;
4132 break;
4133 case '\'':
4134 case '\\':
4135 case '"':
4136 case '$':
4137 /* These can only happen inside quotes */
4138 cc[0] = c;
4139 str = cc;
4140 c = '\\';
4141 break;
4142 default:
4143 break;
4144 }
4145 USTPUTC(c, nextc);
4146 checkstr:
4147 if (!str)
4148 continue;
4149 dostr:
4150 while ((c = *str++)) {
4151 USTPUTC(c, nextc);
4152 }
4153 }
4154 if (quoted & 1) {
4155 USTPUTC('"', nextc);
4156 }
4157 *nextc = 0;
4158 cmdnextc = nextc;
4159}
4160
4161/* cmdtxt() and cmdlist() call each other */
4162static void cmdtxt(union node *n);
4163
4164static void
4165cmdlist(union node *np, int sep)
4166{
4167 for (; np; np = np->narg.next) {
4168 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004169 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004170 cmdtxt(np);
4171 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004172 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004173 }
4174}
4175
4176static void
4177cmdtxt(union node *n)
4178{
4179 union node *np;
4180 struct nodelist *lp;
4181 const char *p;
4182 char s[2];
4183
4184 if (!n)
4185 return;
4186 switch (n->type) {
4187 default:
4188#if DEBUG
4189 abort();
4190#endif
4191 case NPIPE:
4192 lp = n->npipe.cmdlist;
4193 for (;;) {
4194 cmdtxt(lp->n);
4195 lp = lp->next;
4196 if (!lp)
4197 break;
4198 cmdputs(" | ");
4199 }
4200 break;
4201 case NSEMI:
4202 p = "; ";
4203 goto binop;
4204 case NAND:
4205 p = " && ";
4206 goto binop;
4207 case NOR:
4208 p = " || ";
4209 binop:
4210 cmdtxt(n->nbinary.ch1);
4211 cmdputs(p);
4212 n = n->nbinary.ch2;
4213 goto donode;
4214 case NREDIR:
4215 case NBACKGND:
4216 n = n->nredir.n;
4217 goto donode;
4218 case NNOT:
4219 cmdputs("!");
4220 n = n->nnot.com;
4221 donode:
4222 cmdtxt(n);
4223 break;
4224 case NIF:
4225 cmdputs("if ");
4226 cmdtxt(n->nif.test);
4227 cmdputs("; then ");
4228 n = n->nif.ifpart;
4229 if (n->nif.elsepart) {
4230 cmdtxt(n);
4231 cmdputs("; else ");
4232 n = n->nif.elsepart;
4233 }
4234 p = "; fi";
4235 goto dotail;
4236 case NSUBSHELL:
4237 cmdputs("(");
4238 n = n->nredir.n;
4239 p = ")";
4240 goto dotail;
4241 case NWHILE:
4242 p = "while ";
4243 goto until;
4244 case NUNTIL:
4245 p = "until ";
4246 until:
4247 cmdputs(p);
4248 cmdtxt(n->nbinary.ch1);
4249 n = n->nbinary.ch2;
4250 p = "; done";
4251 dodo:
4252 cmdputs("; do ");
4253 dotail:
4254 cmdtxt(n);
4255 goto dotail2;
4256 case NFOR:
4257 cmdputs("for ");
4258 cmdputs(n->nfor.var);
4259 cmdputs(" in ");
4260 cmdlist(n->nfor.args, 1);
4261 n = n->nfor.body;
4262 p = "; done";
4263 goto dodo;
4264 case NDEFUN:
4265 cmdputs(n->narg.text);
4266 p = "() { ... }";
4267 goto dotail2;
4268 case NCMD:
4269 cmdlist(n->ncmd.args, 1);
4270 cmdlist(n->ncmd.redirect, 0);
4271 break;
4272 case NARG:
4273 p = n->narg.text;
4274 dotail2:
4275 cmdputs(p);
4276 break;
4277 case NHERE:
4278 case NXHERE:
4279 p = "<<...";
4280 goto dotail2;
4281 case NCASE:
4282 cmdputs("case ");
4283 cmdputs(n->ncase.expr->narg.text);
4284 cmdputs(" in ");
4285 for (np = n->ncase.cases; np; np = np->nclist.next) {
4286 cmdtxt(np->nclist.pattern);
4287 cmdputs(") ");
4288 cmdtxt(np->nclist.body);
4289 cmdputs(";; ");
4290 }
4291 p = "esac";
4292 goto dotail2;
4293 case NTO:
4294 p = ">";
4295 goto redir;
4296 case NCLOBBER:
4297 p = ">|";
4298 goto redir;
4299 case NAPPEND:
4300 p = ">>";
4301 goto redir;
4302 case NTOFD:
4303 p = ">&";
4304 goto redir;
4305 case NFROM:
4306 p = "<";
4307 goto redir;
4308 case NFROMFD:
4309 p = "<&";
4310 goto redir;
4311 case NFROMTO:
4312 p = "<>";
4313 redir:
4314 s[0] = n->nfile.fd + '0';
4315 s[1] = '\0';
4316 cmdputs(s);
4317 cmdputs(p);
4318 if (n->type == NTOFD || n->type == NFROMFD) {
4319 s[0] = n->ndup.dupfd + '0';
4320 p = s;
4321 goto dotail2;
4322 }
4323 n = n->nfile.fname;
4324 goto donode;
4325 }
4326}
4327
4328static char *
4329commandtext(union node *n)
4330{
4331 char *name;
4332
4333 STARTSTACKSTR(cmdnextc);
4334 cmdtxt(n);
4335 name = stackblock();
4336 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4337 name, cmdnextc, cmdnextc));
4338 return ckstrdup(name);
4339}
4340#endif /* JOBS */
4341
4342/*
4343 * Fork off a subshell. If we are doing job control, give the subshell its
4344 * own process group. Jp is a job structure that the job is to be added to.
4345 * N is the command that will be evaluated by the child. Both jp and n may
4346 * be NULL. The mode parameter can be one of the following:
4347 * FORK_FG - Fork off a foreground process.
4348 * FORK_BG - Fork off a background process.
4349 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4350 * process group even if job control is on.
4351 *
4352 * When job control is turned off, background processes have their standard
4353 * input redirected to /dev/null (except for the second and later processes
4354 * in a pipeline).
4355 *
4356 * Called with interrupts off.
4357 */
4358/*
4359 * Clear traps on a fork.
4360 */
4361static void
4362clear_traps(void)
4363{
4364 char **tp;
4365
4366 for (tp = trap; tp < &trap[NSIG]; tp++) {
4367 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4368 INT_OFF;
4369 free(*tp);
4370 *tp = NULL;
4371 if (tp != &trap[0])
4372 setsignal(tp - trap);
4373 INT_ON;
4374 }
4375 }
4376}
4377/* lives far away from here, needed for forkchild */
4378static void closescript(void);
4379static void
4380forkchild(struct job *jp, union node *n, int mode)
4381{
4382 int oldlvl;
4383
4384 TRACE(("Child shell %d\n", getpid()));
4385 oldlvl = shlvl;
4386 shlvl++;
4387
4388 closescript();
4389 clear_traps();
4390#if JOBS
4391 /* do job control only in root shell */
4392 jobctl = 0;
4393 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4394 pid_t pgrp;
4395
4396 if (jp->nprocs == 0)
4397 pgrp = getpid();
4398 else
4399 pgrp = jp->ps[0].pid;
4400 /* This can fail because we are doing it in the parent also */
4401 (void)setpgid(0, pgrp);
4402 if (mode == FORK_FG)
4403 xtcsetpgrp(ttyfd, pgrp);
4404 setsignal(SIGTSTP);
4405 setsignal(SIGTTOU);
4406 } else
4407#endif
4408 if (mode == FORK_BG) {
4409 ignoresig(SIGINT);
4410 ignoresig(SIGQUIT);
4411 if (jp->nprocs == 0) {
4412 close(0);
4413 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004414 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004415 }
4416 }
4417 if (!oldlvl && iflag) {
4418 setsignal(SIGINT);
4419 setsignal(SIGQUIT);
4420 setsignal(SIGTERM);
4421 }
4422 for (jp = curjob; jp; jp = jp->prev_job)
4423 freejob(jp);
4424 jobless = 0;
4425}
4426
4427static void
4428forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4429{
4430 TRACE(("In parent shell: child = %d\n", pid));
4431 if (!jp) {
4432 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4433 jobless++;
4434 return;
4435 }
4436#if JOBS
4437 if (mode != FORK_NOJOB && jp->jobctl) {
4438 int pgrp;
4439
4440 if (jp->nprocs == 0)
4441 pgrp = pid;
4442 else
4443 pgrp = jp->ps[0].pid;
4444 /* This can fail because we are doing it in the child also */
4445 setpgid(pid, pgrp);
4446 }
4447#endif
4448 if (mode == FORK_BG) {
4449 backgndpid = pid; /* set $! */
4450 set_curjob(jp, CUR_RUNNING);
4451 }
4452 if (jp) {
4453 struct procstat *ps = &jp->ps[jp->nprocs++];
4454 ps->pid = pid;
4455 ps->status = -1;
4456 ps->cmd = nullstr;
4457#if JOBS
4458 if (jobctl && n)
4459 ps->cmd = commandtext(n);
4460#endif
4461 }
4462}
4463
4464static int
4465forkshell(struct job *jp, union node *n, int mode)
4466{
4467 int pid;
4468
4469 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4470 pid = fork();
4471 if (pid < 0) {
4472 TRACE(("Fork failed, errno=%d", errno));
4473 if (jp)
4474 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004475 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004476 }
4477 if (pid == 0)
4478 forkchild(jp, n, mode);
4479 else
4480 forkparent(jp, n, mode, pid);
4481 return pid;
4482}
4483
4484/*
4485 * Wait for job to finish.
4486 *
4487 * Under job control we have the problem that while a child process is
4488 * running interrupts generated by the user are sent to the child but not
4489 * to the shell. This means that an infinite loop started by an inter-
4490 * active user may be hard to kill. With job control turned off, an
4491 * interactive user may place an interactive program inside a loop. If
4492 * the interactive program catches interrupts, the user doesn't want
4493 * these interrupts to also abort the loop. The approach we take here
4494 * is to have the shell ignore interrupt signals while waiting for a
4495 * foreground process to terminate, and then send itself an interrupt
4496 * signal if the child process was terminated by an interrupt signal.
4497 * Unfortunately, some programs want to do a bit of cleanup and then
4498 * exit on interrupt; unless these processes terminate themselves by
4499 * sending a signal to themselves (instead of calling exit) they will
4500 * confuse this approach.
4501 *
4502 * Called with interrupts off.
4503 */
4504static int
4505waitforjob(struct job *jp)
4506{
4507 int st;
4508
4509 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4510 while (jp->state == JOBRUNNING) {
4511 dowait(DOWAIT_BLOCK, jp);
4512 }
4513 st = getstatus(jp);
4514#if JOBS
4515 if (jp->jobctl) {
4516 xtcsetpgrp(ttyfd, rootpid);
4517 /*
4518 * This is truly gross.
4519 * If we're doing job control, then we did a TIOCSPGRP which
4520 * caused us (the shell) to no longer be in the controlling
4521 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4522 * intuit from the subprocess exit status whether a SIGINT
4523 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4524 */
4525 if (jp->sigint)
4526 raise(SIGINT);
4527 }
4528 if (jp->state == JOBDONE)
4529#endif
4530 freejob(jp);
4531 return st;
4532}
4533
4534/*
4535 * return 1 if there are stopped jobs, otherwise 0
4536 */
4537static int
4538stoppedjobs(void)
4539{
4540 struct job *jp;
4541 int retval;
4542
4543 retval = 0;
4544 if (job_warning)
4545 goto out;
4546 jp = curjob;
4547 if (jp && jp->state == JOBSTOPPED) {
4548 out2str("You have stopped jobs.\n");
4549 job_warning = 2;
4550 retval++;
4551 }
4552 out:
4553 return retval;
4554}
4555
4556
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004557/* ============ redir.c
4558 *
4559 * Code for dealing with input/output redirection.
4560 */
4561
4562#define EMPTY -2 /* marks an unused slot in redirtab */
4563#ifndef PIPE_BUF
4564# define PIPESIZE 4096 /* amount of buffering in a pipe */
4565#else
4566# define PIPESIZE PIPE_BUF
4567#endif
4568
4569/*
4570 * Open a file in noclobber mode.
4571 * The code was copied from bash.
4572 */
4573static int
4574noclobberopen(const char *fname)
4575{
4576 int r, fd;
4577 struct stat finfo, finfo2;
4578
4579 /*
4580 * If the file exists and is a regular file, return an error
4581 * immediately.
4582 */
4583 r = stat(fname, &finfo);
4584 if (r == 0 && S_ISREG(finfo.st_mode)) {
4585 errno = EEXIST;
4586 return -1;
4587 }
4588
4589 /*
4590 * If the file was not present (r != 0), make sure we open it
4591 * exclusively so that if it is created before we open it, our open
4592 * will fail. Make sure that we do not truncate an existing file.
4593 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4594 * file was not a regular file, we leave O_EXCL off.
4595 */
4596 if (r != 0)
4597 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4598 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4599
4600 /* If the open failed, return the file descriptor right away. */
4601 if (fd < 0)
4602 return fd;
4603
4604 /*
4605 * OK, the open succeeded, but the file may have been changed from a
4606 * non-regular file to a regular file between the stat and the open.
4607 * We are assuming that the O_EXCL open handles the case where FILENAME
4608 * did not exist and is symlinked to an existing file between the stat
4609 * and open.
4610 */
4611
4612 /*
4613 * If we can open it and fstat the file descriptor, and neither check
4614 * revealed that it was a regular file, and the file has not been
4615 * replaced, return the file descriptor.
4616 */
4617 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4618 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4619 return fd;
4620
4621 /* The file has been replaced. badness. */
4622 close(fd);
4623 errno = EEXIST;
4624 return -1;
4625}
4626
4627/*
4628 * Handle here documents. Normally we fork off a process to write the
4629 * data to a pipe. If the document is short, we can stuff the data in
4630 * the pipe without forking.
4631 */
4632/* openhere needs this forward reference */
4633static void expandhere(union node *arg, int fd);
4634static int
4635openhere(union node *redir)
4636{
4637 int pip[2];
4638 size_t len = 0;
4639
4640 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004641 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004642 if (redir->type == NHERE) {
4643 len = strlen(redir->nhere.doc->narg.text);
4644 if (len <= PIPESIZE) {
4645 full_write(pip[1], redir->nhere.doc->narg.text, len);
4646 goto out;
4647 }
4648 }
4649 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4650 close(pip[0]);
4651 signal(SIGINT, SIG_IGN);
4652 signal(SIGQUIT, SIG_IGN);
4653 signal(SIGHUP, SIG_IGN);
4654#ifdef SIGTSTP
4655 signal(SIGTSTP, SIG_IGN);
4656#endif
4657 signal(SIGPIPE, SIG_DFL);
4658 if (redir->type == NHERE)
4659 full_write(pip[1], redir->nhere.doc->narg.text, len);
4660 else
4661 expandhere(redir->nhere.doc, pip[1]);
4662 _exit(0);
4663 }
4664 out:
4665 close(pip[1]);
4666 return pip[0];
4667}
4668
4669static int
4670openredirect(union node *redir)
4671{
4672 char *fname;
4673 int f;
4674
4675 switch (redir->nfile.type) {
4676 case NFROM:
4677 fname = redir->nfile.expfname;
4678 f = open(fname, O_RDONLY);
4679 if (f < 0)
4680 goto eopen;
4681 break;
4682 case NFROMTO:
4683 fname = redir->nfile.expfname;
4684 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4685 if (f < 0)
4686 goto ecreate;
4687 break;
4688 case NTO:
4689 /* Take care of noclobber mode. */
4690 if (Cflag) {
4691 fname = redir->nfile.expfname;
4692 f = noclobberopen(fname);
4693 if (f < 0)
4694 goto ecreate;
4695 break;
4696 }
4697 /* FALLTHROUGH */
4698 case NCLOBBER:
4699 fname = redir->nfile.expfname;
4700 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4701 if (f < 0)
4702 goto ecreate;
4703 break;
4704 case NAPPEND:
4705 fname = redir->nfile.expfname;
4706 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4707 if (f < 0)
4708 goto ecreate;
4709 break;
4710 default:
4711#if DEBUG
4712 abort();
4713#endif
4714 /* Fall through to eliminate warning. */
4715 case NTOFD:
4716 case NFROMFD:
4717 f = -1;
4718 break;
4719 case NHERE:
4720 case NXHERE:
4721 f = openhere(redir);
4722 break;
4723 }
4724
4725 return f;
4726 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004727 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004728 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004729 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004730}
4731
4732/*
4733 * Copy a file descriptor to be >= to. Returns -1
4734 * if the source file descriptor is closed, EMPTY if there are no unused
4735 * file descriptors left.
4736 */
4737static int
4738copyfd(int from, int to)
4739{
4740 int newfd;
4741
4742 newfd = fcntl(from, F_DUPFD, to);
4743 if (newfd < 0) {
4744 if (errno == EMFILE)
4745 return EMPTY;
4746 ash_msg_and_raise_error("%d: %m", from);
4747 }
4748 return newfd;
4749}
4750
4751static void
4752dupredirect(union node *redir, int f)
4753{
4754 int fd = redir->nfile.fd;
4755
4756 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4757 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4758 copyfd(redir->ndup.dupfd, fd);
4759 }
4760 return;
4761 }
4762
4763 if (f != fd) {
4764 copyfd(f, fd);
4765 close(f);
4766 }
4767}
4768
4769/*
4770 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4771 * old file descriptors are stashed away so that the redirection can be
4772 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4773 * standard output, and the standard error if it becomes a duplicate of
4774 * stdout, is saved in memory.
4775 */
4776/* flags passed to redirect */
4777#define REDIR_PUSH 01 /* save previous values of file descriptors */
4778#define REDIR_SAVEFD2 03 /* set preverrout */
4779static void
4780redirect(union node *redir, int flags)
4781{
4782 union node *n;
4783 struct redirtab *sv;
4784 int i;
4785 int fd;
4786 int newfd;
4787 int *p;
4788 nullredirs++;
4789 if (!redir) {
4790 return;
4791 }
4792 sv = NULL;
4793 INT_OFF;
4794 if (flags & REDIR_PUSH) {
4795 struct redirtab *q;
4796 q = ckmalloc(sizeof(struct redirtab));
4797 q->next = redirlist;
4798 redirlist = q;
4799 q->nullredirs = nullredirs - 1;
4800 for (i = 0; i < 10; i++)
4801 q->renamed[i] = EMPTY;
4802 nullredirs = 0;
4803 sv = q;
4804 }
4805 n = redir;
4806 do {
4807 fd = n->nfile.fd;
4808 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4809 && n->ndup.dupfd == fd)
4810 continue; /* redirect from/to same file descriptor */
4811
4812 newfd = openredirect(n);
4813 if (fd == newfd)
4814 continue;
4815 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4816 i = fcntl(fd, F_DUPFD, 10);
4817
4818 if (i == -1) {
4819 i = errno;
4820 if (i != EBADF) {
4821 close(newfd);
4822 errno = i;
4823 ash_msg_and_raise_error("%d: %m", fd);
4824 /* NOTREACHED */
4825 }
4826 } else {
4827 *p = i;
4828 close(fd);
4829 }
4830 } else {
4831 close(fd);
4832 }
4833 dupredirect(n, newfd);
4834 } while ((n = n->nfile.next));
4835 INT_ON;
4836 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4837 preverrout_fd = sv->renamed[2];
4838}
4839
4840/*
4841 * Undo the effects of the last redirection.
4842 */
4843static void
4844popredir(int drop)
4845{
4846 struct redirtab *rp;
4847 int i;
4848
4849 if (--nullredirs >= 0)
4850 return;
4851 INT_OFF;
4852 rp = redirlist;
4853 for (i = 0; i < 10; i++) {
4854 if (rp->renamed[i] != EMPTY) {
4855 if (!drop) {
4856 close(i);
4857 copyfd(rp->renamed[i], i);
4858 }
4859 close(rp->renamed[i]);
4860 }
4861 }
4862 redirlist = rp->next;
4863 nullredirs = rp->nullredirs;
4864 free(rp);
4865 INT_ON;
4866}
4867
4868/*
4869 * Undo all redirections. Called on error or interrupt.
4870 */
4871
4872/*
4873 * Discard all saved file descriptors.
4874 */
4875static void
4876clearredir(int drop)
4877{
4878 for (;;) {
4879 nullredirs = 0;
4880 if (!redirlist)
4881 break;
4882 popredir(drop);
4883 }
4884}
4885
4886static int
4887redirectsafe(union node *redir, int flags)
4888{
4889 int err;
4890 volatile int saveint;
4891 struct jmploc *volatile savehandler = exception_handler;
4892 struct jmploc jmploc;
4893
4894 SAVE_INT(saveint);
4895 err = setjmp(jmploc.loc) * 2;
4896 if (!err) {
4897 exception_handler = &jmploc;
4898 redirect(redir, flags);
4899 }
4900 exception_handler = savehandler;
4901 if (err && exception != EXERROR)
4902 longjmp(exception_handler->loc, 1);
4903 RESTORE_INT(saveint);
4904 return err;
4905}
4906
4907
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004908/* ============ Routines to expand arguments to commands
4909 *
4910 * We have to deal with backquotes, shell variables, and file metacharacters.
4911 */
4912
4913/*
4914 * expandarg flags
4915 */
4916#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4917#define EXP_TILDE 0x2 /* do normal tilde expansion */
4918#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4919#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4920#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4921#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4922#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4923#define EXP_WORD 0x80 /* expand word in parameter expansion */
4924#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4925/*
4926 * _rmescape() flags
4927 */
4928#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4929#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4930#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4931#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4932#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4933
4934/*
4935 * Structure specifying which parts of the string should be searched
4936 * for IFS characters.
4937 */
4938struct ifsregion {
4939 struct ifsregion *next; /* next region in list */
4940 int begoff; /* offset of start of region */
4941 int endoff; /* offset of end of region */
4942 int nulonly; /* search for nul bytes only */
4943};
4944
4945struct arglist {
4946 struct strlist *list;
4947 struct strlist **lastp;
4948};
4949
4950/* output of current string */
4951static char *expdest;
4952/* list of back quote expressions */
4953static struct nodelist *argbackq;
4954/* first struct in list of ifs regions */
4955static struct ifsregion ifsfirst;
4956/* last struct in list */
4957static struct ifsregion *ifslastp;
4958/* holds expanded arg list */
4959static struct arglist exparg;
4960
4961/*
4962 * Our own itoa().
4963 */
4964static int
4965cvtnum(arith_t num)
4966{
4967 int len;
4968
4969 expdest = makestrspace(32, expdest);
4970#if ENABLE_ASH_MATH_SUPPORT_64
4971 len = fmtstr(expdest, 32, "%lld", (long long) num);
4972#else
4973 len = fmtstr(expdest, 32, "%ld", num);
4974#endif
4975 STADJUST(len, expdest);
4976 return len;
4977}
4978
4979static size_t
4980esclen(const char *start, const char *p)
4981{
4982 size_t esc = 0;
4983
4984 while (p > start && *--p == CTLESC) {
4985 esc++;
4986 }
4987 return esc;
4988}
4989
4990/*
4991 * Remove any CTLESC characters from a string.
4992 */
4993static char *
4994_rmescapes(char *str, int flag)
4995{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004996 static const char qchars[] = { CTLESC, CTLQUOTEMARK, '\0' };
4997
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004998 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004999 unsigned inquotes;
5000 int notescaped;
5001 int globbing;
5002
5003 p = strpbrk(str, qchars);
5004 if (!p) {
5005 return str;
5006 }
5007 q = p;
5008 r = str;
5009 if (flag & RMESCAPE_ALLOC) {
5010 size_t len = p - str;
5011 size_t fulllen = len + strlen(p) + 1;
5012
5013 if (flag & RMESCAPE_GROW) {
5014 r = makestrspace(fulllen, expdest);
5015 } else if (flag & RMESCAPE_HEAP) {
5016 r = ckmalloc(fulllen);
5017 } else {
5018 r = stalloc(fulllen);
5019 }
5020 q = r;
5021 if (len > 0) {
5022 q = memcpy(q, str, len) + len;
5023 }
5024 }
5025 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5026 globbing = flag & RMESCAPE_GLOB;
5027 notescaped = globbing;
5028 while (*p) {
5029 if (*p == CTLQUOTEMARK) {
5030 inquotes = ~inquotes;
5031 p++;
5032 notescaped = globbing;
5033 continue;
5034 }
5035 if (*p == '\\') {
5036 /* naked back slash */
5037 notescaped = 0;
5038 goto copy;
5039 }
5040 if (*p == CTLESC) {
5041 p++;
5042 if (notescaped && inquotes && *p != '/') {
5043 *q++ = '\\';
5044 }
5045 }
5046 notescaped = globbing;
5047 copy:
5048 *q++ = *p++;
5049 }
5050 *q = '\0';
5051 if (flag & RMESCAPE_GROW) {
5052 expdest = r;
5053 STADJUST(q - r + 1, expdest);
5054 }
5055 return r;
5056}
5057#define rmescapes(p) _rmescapes((p), 0)
5058
5059#define pmatch(a, b) !fnmatch((a), (b), 0)
5060
5061/*
5062 * Prepare a pattern for a expmeta (internal glob(3)) call.
5063 *
5064 * Returns an stalloced string.
5065 */
5066static char *
5067preglob(const char *pattern, int quoted, int flag)
5068{
5069 flag |= RMESCAPE_GLOB;
5070 if (quoted) {
5071 flag |= RMESCAPE_QUOTED;
5072 }
5073 return _rmescapes((char *)pattern, flag);
5074}
5075
5076/*
5077 * Put a string on the stack.
5078 */
5079static void
5080memtodest(const char *p, size_t len, int syntax, int quotes)
5081{
5082 char *q = expdest;
5083
5084 q = makestrspace(len * 2, q);
5085
5086 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005087 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005088 if (!c)
5089 continue;
5090 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5091 USTPUTC(CTLESC, q);
5092 USTPUTC(c, q);
5093 }
5094
5095 expdest = q;
5096}
5097
5098static void
5099strtodest(const char *p, int syntax, int quotes)
5100{
5101 memtodest(p, strlen(p), syntax, quotes);
5102}
5103
5104/*
5105 * Record the fact that we have to scan this region of the
5106 * string for IFS characters.
5107 */
5108static void
5109recordregion(int start, int end, int nulonly)
5110{
5111 struct ifsregion *ifsp;
5112
5113 if (ifslastp == NULL) {
5114 ifsp = &ifsfirst;
5115 } else {
5116 INT_OFF;
5117 ifsp = ckmalloc(sizeof(*ifsp));
5118 ifsp->next = NULL;
5119 ifslastp->next = ifsp;
5120 INT_ON;
5121 }
5122 ifslastp = ifsp;
5123 ifslastp->begoff = start;
5124 ifslastp->endoff = end;
5125 ifslastp->nulonly = nulonly;
5126}
5127
5128static void
5129removerecordregions(int endoff)
5130{
5131 if (ifslastp == NULL)
5132 return;
5133
5134 if (ifsfirst.endoff > endoff) {
5135 while (ifsfirst.next != NULL) {
5136 struct ifsregion *ifsp;
5137 INT_OFF;
5138 ifsp = ifsfirst.next->next;
5139 free(ifsfirst.next);
5140 ifsfirst.next = ifsp;
5141 INT_ON;
5142 }
5143 if (ifsfirst.begoff > endoff)
5144 ifslastp = NULL;
5145 else {
5146 ifslastp = &ifsfirst;
5147 ifsfirst.endoff = endoff;
5148 }
5149 return;
5150 }
5151
5152 ifslastp = &ifsfirst;
5153 while (ifslastp->next && ifslastp->next->begoff < endoff)
5154 ifslastp=ifslastp->next;
5155 while (ifslastp->next != NULL) {
5156 struct ifsregion *ifsp;
5157 INT_OFF;
5158 ifsp = ifslastp->next->next;
5159 free(ifslastp->next);
5160 ifslastp->next = ifsp;
5161 INT_ON;
5162 }
5163 if (ifslastp->endoff > endoff)
5164 ifslastp->endoff = endoff;
5165}
5166
5167static char *
5168exptilde(char *startp, char *p, int flag)
5169{
5170 char c;
5171 char *name;
5172 struct passwd *pw;
5173 const char *home;
5174 int quotes = flag & (EXP_FULL | EXP_CASE);
5175 int startloc;
5176
5177 name = p + 1;
5178
5179 while ((c = *++p) != '\0') {
5180 switch (c) {
5181 case CTLESC:
5182 return startp;
5183 case CTLQUOTEMARK:
5184 return startp;
5185 case ':':
5186 if (flag & EXP_VARTILDE)
5187 goto done;
5188 break;
5189 case '/':
5190 case CTLENDVAR:
5191 goto done;
5192 }
5193 }
5194 done:
5195 *p = '\0';
5196 if (*name == '\0') {
5197 home = lookupvar(homestr);
5198 } else {
5199 pw = getpwnam(name);
5200 if (pw == NULL)
5201 goto lose;
5202 home = pw->pw_dir;
5203 }
5204 if (!home || !*home)
5205 goto lose;
5206 *p = c;
5207 startloc = expdest - (char *)stackblock();
5208 strtodest(home, SQSYNTAX, quotes);
5209 recordregion(startloc, expdest - (char *)stackblock(), 0);
5210 return p;
5211 lose:
5212 *p = c;
5213 return startp;
5214}
5215
5216/*
5217 * Execute a command inside back quotes. If it's a builtin command, we
5218 * want to save its output in a block obtained from malloc. Otherwise
5219 * we fork off a subprocess and get the output of the command via a pipe.
5220 * Should be called with interrupts off.
5221 */
5222struct backcmd { /* result of evalbackcmd */
5223 int fd; /* file descriptor to read from */
5224 char *buf; /* buffer */
5225 int nleft; /* number of chars in buffer */
5226 struct job *jp; /* job structure for command */
5227};
5228
5229/* These forward decls are needed to use "eval" code for backticks handling: */
5230static int back_exitstatus; /* exit status of backquoted command */
5231#define EV_EXIT 01 /* exit after evaluating tree */
5232static void evaltree(union node *, int);
5233
5234static void
5235evalbackcmd(union node *n, struct backcmd *result)
5236{
5237 int saveherefd;
5238
5239 result->fd = -1;
5240 result->buf = NULL;
5241 result->nleft = 0;
5242 result->jp = NULL;
5243 if (n == NULL) {
5244 goto out;
5245 }
5246
5247 saveherefd = herefd;
5248 herefd = -1;
5249
5250 {
5251 int pip[2];
5252 struct job *jp;
5253
5254 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005255 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005256 jp = makejob(n, 1);
5257 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5258 FORCE_INT_ON;
5259 close(pip[0]);
5260 if (pip[1] != 1) {
5261 close(1);
5262 copyfd(pip[1], 1);
5263 close(pip[1]);
5264 }
5265 eflag = 0;
5266 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5267 /* NOTREACHED */
5268 }
5269 close(pip[1]);
5270 result->fd = pip[0];
5271 result->jp = jp;
5272 }
5273 herefd = saveherefd;
5274 out:
5275 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5276 result->fd, result->buf, result->nleft, result->jp));
5277}
5278
5279/*
5280 * Expand stuff in backwards quotes.
5281 */
5282static void
5283expbackq(union node *cmd, int quoted, int quotes)
5284{
5285 struct backcmd in;
5286 int i;
5287 char buf[128];
5288 char *p;
5289 char *dest;
5290 int startloc;
5291 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5292 struct stackmark smark;
5293
5294 INT_OFF;
5295 setstackmark(&smark);
5296 dest = expdest;
5297 startloc = dest - (char *)stackblock();
5298 grabstackstr(dest);
5299 evalbackcmd(cmd, &in);
5300 popstackmark(&smark);
5301
5302 p = in.buf;
5303 i = in.nleft;
5304 if (i == 0)
5305 goto read;
5306 for (;;) {
5307 memtodest(p, i, syntax, quotes);
5308 read:
5309 if (in.fd < 0)
5310 break;
5311 i = safe_read(in.fd, buf, sizeof(buf));
5312 TRACE(("expbackq: read returns %d\n", i));
5313 if (i <= 0)
5314 break;
5315 p = buf;
5316 }
5317
5318 if (in.buf)
5319 free(in.buf);
5320 if (in.fd >= 0) {
5321 close(in.fd);
5322 back_exitstatus = waitforjob(in.jp);
5323 }
5324 INT_ON;
5325
5326 /* Eat all trailing newlines */
5327 dest = expdest;
5328 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5329 STUNPUTC(dest);
5330 expdest = dest;
5331
5332 if (quoted == 0)
5333 recordregion(startloc, dest - (char *)stackblock(), 0);
5334 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5335 (dest - (char *)stackblock()) - startloc,
5336 (dest - (char *)stackblock()) - startloc,
5337 stackblock() + startloc));
5338}
5339
5340#if ENABLE_ASH_MATH_SUPPORT
5341/*
5342 * Expand arithmetic expression. Backup to start of expression,
5343 * evaluate, place result in (backed up) result, adjust string position.
5344 */
5345static void
5346expari(int quotes)
5347{
5348 char *p, *start;
5349 int begoff;
5350 int flag;
5351 int len;
5352
5353 /* ifsfree(); */
5354
5355 /*
5356 * This routine is slightly over-complicated for
5357 * efficiency. Next we scan backwards looking for the
5358 * start of arithmetic.
5359 */
5360 start = stackblock();
5361 p = expdest - 1;
5362 *p = '\0';
5363 p--;
5364 do {
5365 int esc;
5366
5367 while (*p != CTLARI) {
5368 p--;
5369#if DEBUG
5370 if (p < start) {
5371 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5372 }
5373#endif
5374 }
5375
5376 esc = esclen(start, p);
5377 if (!(esc % 2)) {
5378 break;
5379 }
5380
5381 p -= esc + 1;
5382 } while (1);
5383
5384 begoff = p - start;
5385
5386 removerecordregions(begoff);
5387
5388 flag = p[1];
5389
5390 expdest = p;
5391
5392 if (quotes)
5393 rmescapes(p + 2);
5394
5395 len = cvtnum(dash_arith(p + 2));
5396
5397 if (flag != '"')
5398 recordregion(begoff, begoff + len, 0);
5399}
5400#endif
5401
5402/* argstr needs it */
5403static char *evalvar(char *p, int flag);
5404
5405/*
5406 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5407 * characters to allow for further processing. Otherwise treat
5408 * $@ like $* since no splitting will be performed.
5409 */
5410static void
5411argstr(char *p, int flag)
5412{
5413 static const char spclchars[] = {
5414 '=',
5415 ':',
5416 CTLQUOTEMARK,
5417 CTLENDVAR,
5418 CTLESC,
5419 CTLVAR,
5420 CTLBACKQ,
5421 CTLBACKQ | CTLQUOTE,
5422#if ENABLE_ASH_MATH_SUPPORT
5423 CTLENDARI,
5424#endif
5425 0
5426 };
5427 const char *reject = spclchars;
5428 int c;
5429 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5430 int breakall = flag & EXP_WORD;
5431 int inquotes;
5432 size_t length;
5433 int startloc;
5434
5435 if (!(flag & EXP_VARTILDE)) {
5436 reject += 2;
5437 } else if (flag & EXP_VARTILDE2) {
5438 reject++;
5439 }
5440 inquotes = 0;
5441 length = 0;
5442 if (flag & EXP_TILDE) {
5443 char *q;
5444
5445 flag &= ~EXP_TILDE;
5446 tilde:
5447 q = p;
5448 if (*q == CTLESC && (flag & EXP_QWORD))
5449 q++;
5450 if (*q == '~')
5451 p = exptilde(p, q, flag);
5452 }
5453 start:
5454 startloc = expdest - (char *)stackblock();
5455 for (;;) {
5456 length += strcspn(p + length, reject);
5457 c = p[length];
5458 if (c && (!(c & 0x80)
5459#if ENABLE_ASH_MATH_SUPPORT
5460 || c == CTLENDARI
5461#endif
5462 )) {
5463 /* c == '=' || c == ':' || c == CTLENDARI */
5464 length++;
5465 }
5466 if (length > 0) {
5467 int newloc;
5468 expdest = stack_nputstr(p, length, expdest);
5469 newloc = expdest - (char *)stackblock();
5470 if (breakall && !inquotes && newloc > startloc) {
5471 recordregion(startloc, newloc, 0);
5472 }
5473 startloc = newloc;
5474 }
5475 p += length + 1;
5476 length = 0;
5477
5478 switch (c) {
5479 case '\0':
5480 goto breakloop;
5481 case '=':
5482 if (flag & EXP_VARTILDE2) {
5483 p--;
5484 continue;
5485 }
5486 flag |= EXP_VARTILDE2;
5487 reject++;
5488 /* fall through */
5489 case ':':
5490 /*
5491 * sort of a hack - expand tildes in variable
5492 * assignments (after the first '=' and after ':'s).
5493 */
5494 if (*--p == '~') {
5495 goto tilde;
5496 }
5497 continue;
5498 }
5499
5500 switch (c) {
5501 case CTLENDVAR: /* ??? */
5502 goto breakloop;
5503 case CTLQUOTEMARK:
5504 /* "$@" syntax adherence hack */
5505 if (
5506 !inquotes &&
5507 !memcmp(p, dolatstr, 4) &&
5508 (p[4] == CTLQUOTEMARK || (
5509 p[4] == CTLENDVAR &&
5510 p[5] == CTLQUOTEMARK
5511 ))
5512 ) {
5513 p = evalvar(p + 1, flag) + 1;
5514 goto start;
5515 }
5516 inquotes = !inquotes;
5517 addquote:
5518 if (quotes) {
5519 p--;
5520 length++;
5521 startloc++;
5522 }
5523 break;
5524 case CTLESC:
5525 startloc++;
5526 length++;
5527 goto addquote;
5528 case CTLVAR:
5529 p = evalvar(p, flag);
5530 goto start;
5531 case CTLBACKQ:
5532 c = 0;
5533 case CTLBACKQ|CTLQUOTE:
5534 expbackq(argbackq->n, c, quotes);
5535 argbackq = argbackq->next;
5536 goto start;
5537#if ENABLE_ASH_MATH_SUPPORT
5538 case CTLENDARI:
5539 p--;
5540 expari(quotes);
5541 goto start;
5542#endif
5543 }
5544 }
5545 breakloop:
5546 ;
5547}
5548
5549static char *
5550scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5551 int zero)
5552{
5553 char *loc;
5554 char *loc2;
5555 char c;
5556
5557 loc = startp;
5558 loc2 = rmesc;
5559 do {
5560 int match;
5561 const char *s = loc2;
5562 c = *loc2;
5563 if (zero) {
5564 *loc2 = '\0';
5565 s = rmesc;
5566 }
5567 match = pmatch(str, s);
5568 *loc2 = c;
5569 if (match)
5570 return loc;
5571 if (quotes && *loc == CTLESC)
5572 loc++;
5573 loc++;
5574 loc2++;
5575 } while (c);
5576 return 0;
5577}
5578
5579static char *
5580scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5581 int zero)
5582{
5583 int esc = 0;
5584 char *loc;
5585 char *loc2;
5586
5587 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5588 int match;
5589 char c = *loc2;
5590 const char *s = loc2;
5591 if (zero) {
5592 *loc2 = '\0';
5593 s = rmesc;
5594 }
5595 match = pmatch(str, s);
5596 *loc2 = c;
5597 if (match)
5598 return loc;
5599 loc--;
5600 if (quotes) {
5601 if (--esc < 0) {
5602 esc = esclen(startp, loc);
5603 }
5604 if (esc % 2) {
5605 esc--;
5606 loc--;
5607 }
5608 }
5609 }
5610 return 0;
5611}
5612
5613static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5614static void
5615varunset(const char *end, const char *var, const char *umsg, int varflags)
5616{
5617 const char *msg;
5618 const char *tail;
5619
5620 tail = nullstr;
5621 msg = "parameter not set";
5622 if (umsg) {
5623 if (*end == CTLENDVAR) {
5624 if (varflags & VSNUL)
5625 tail = " or null";
5626 } else
5627 msg = umsg;
5628 }
5629 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5630}
5631
5632static const char *
5633subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5634{
5635 char *startp;
5636 char *loc;
5637 int saveherefd = herefd;
5638 struct nodelist *saveargbackq = argbackq;
5639 int amount;
5640 char *rmesc, *rmescend;
5641 int zero;
5642 char *(*scan)(char *, char *, char *, char *, int , int);
5643
5644 herefd = -1;
5645 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5646 STPUTC('\0', expdest);
5647 herefd = saveherefd;
5648 argbackq = saveargbackq;
5649 startp = stackblock() + startloc;
5650
5651 switch (subtype) {
5652 case VSASSIGN:
5653 setvar(str, startp, 0);
5654 amount = startp - expdest;
5655 STADJUST(amount, expdest);
5656 return startp;
5657
5658 case VSQUESTION:
5659 varunset(p, str, startp, varflags);
5660 /* NOTREACHED */
5661 }
5662
5663 subtype -= VSTRIMRIGHT;
5664#if DEBUG
5665 if (subtype < 0 || subtype > 3)
5666 abort();
5667#endif
5668
5669 rmesc = startp;
5670 rmescend = stackblock() + strloc;
5671 if (quotes) {
5672 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5673 if (rmesc != startp) {
5674 rmescend = expdest;
5675 startp = stackblock() + startloc;
5676 }
5677 }
5678 rmescend--;
5679 str = stackblock() + strloc;
5680 preglob(str, varflags & VSQUOTE, 0);
5681
5682 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5683 zero = subtype >> 1;
5684 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5685 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5686
5687 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5688 if (loc) {
5689 if (zero) {
5690 memmove(startp, loc, str - loc);
5691 loc = startp + (str - loc) - 1;
5692 }
5693 *loc = '\0';
5694 amount = loc - expdest;
5695 STADJUST(amount, expdest);
5696 }
5697 return loc;
5698}
5699
5700/*
5701 * Add the value of a specialized variable to the stack string.
5702 */
5703static ssize_t
5704varvalue(char *name, int varflags, int flags)
5705{
5706 int num;
5707 char *p;
5708 int i;
5709 int sep = 0;
5710 int sepq = 0;
5711 ssize_t len = 0;
5712 char **ap;
5713 int syntax;
5714 int quoted = varflags & VSQUOTE;
5715 int subtype = varflags & VSTYPE;
5716 int quotes = flags & (EXP_FULL | EXP_CASE);
5717
5718 if (quoted && (flags & EXP_FULL))
5719 sep = 1 << CHAR_BIT;
5720
5721 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5722 switch (*name) {
5723 case '$':
5724 num = rootpid;
5725 goto numvar;
5726 case '?':
5727 num = exitstatus;
5728 goto numvar;
5729 case '#':
5730 num = shellparam.nparam;
5731 goto numvar;
5732 case '!':
5733 num = backgndpid;
5734 if (num == 0)
5735 return -1;
5736 numvar:
5737 len = cvtnum(num);
5738 break;
5739 case '-':
5740 p = makestrspace(NOPTS, expdest);
5741 for (i = NOPTS - 1; i >= 0; i--) {
5742 if (optlist[i]) {
5743 USTPUTC(optletters(i), p);
5744 len++;
5745 }
5746 }
5747 expdest = p;
5748 break;
5749 case '@':
5750 if (sep)
5751 goto param;
5752 /* fall through */
5753 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005754 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005755 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5756 sepq = 1;
5757 param:
5758 ap = shellparam.p;
5759 if (!ap)
5760 return -1;
5761 while ((p = *ap++)) {
5762 size_t partlen;
5763
5764 partlen = strlen(p);
5765 len += partlen;
5766
5767 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5768 memtodest(p, partlen, syntax, quotes);
5769
5770 if (*ap && sep) {
5771 char *q;
5772
5773 len++;
5774 if (subtype == VSPLUS || subtype == VSLENGTH) {
5775 continue;
5776 }
5777 q = expdest;
5778 if (sepq)
5779 STPUTC(CTLESC, q);
5780 STPUTC(sep, q);
5781 expdest = q;
5782 }
5783 }
5784 return len;
5785 case '0':
5786 case '1':
5787 case '2':
5788 case '3':
5789 case '4':
5790 case '5':
5791 case '6':
5792 case '7':
5793 case '8':
5794 case '9':
5795 num = atoi(name);
5796 if (num < 0 || num > shellparam.nparam)
5797 return -1;
5798 p = num ? shellparam.p[num - 1] : arg0;
5799 goto value;
5800 default:
5801 p = lookupvar(name);
5802 value:
5803 if (!p)
5804 return -1;
5805
5806 len = strlen(p);
5807 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5808 memtodest(p, len, syntax, quotes);
5809 return len;
5810 }
5811
5812 if (subtype == VSPLUS || subtype == VSLENGTH)
5813 STADJUST(-len, expdest);
5814 return len;
5815}
5816
5817/*
5818 * Expand a variable, and return a pointer to the next character in the
5819 * input string.
5820 */
5821static char *
5822evalvar(char *p, int flag)
5823{
5824 int subtype;
5825 int varflags;
5826 char *var;
5827 int patloc;
5828 int c;
5829 int startloc;
5830 ssize_t varlen;
5831 int easy;
5832 int quotes;
5833 int quoted;
5834
5835 quotes = flag & (EXP_FULL | EXP_CASE);
5836 varflags = *p++;
5837 subtype = varflags & VSTYPE;
5838 quoted = varflags & VSQUOTE;
5839 var = p;
5840 easy = (!quoted || (*var == '@' && shellparam.nparam));
5841 startloc = expdest - (char *)stackblock();
5842 p = strchr(p, '=') + 1;
5843
5844 again:
5845 varlen = varvalue(var, varflags, flag);
5846 if (varflags & VSNUL)
5847 varlen--;
5848
5849 if (subtype == VSPLUS) {
5850 varlen = -1 - varlen;
5851 goto vsplus;
5852 }
5853
5854 if (subtype == VSMINUS) {
5855 vsplus:
5856 if (varlen < 0) {
5857 argstr(
5858 p, flag | EXP_TILDE |
5859 (quoted ? EXP_QWORD : EXP_WORD)
5860 );
5861 goto end;
5862 }
5863 if (easy)
5864 goto record;
5865 goto end;
5866 }
5867
5868 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5869 if (varlen < 0) {
5870 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5871 varflags &= ~VSNUL;
5872 /*
5873 * Remove any recorded regions beyond
5874 * start of variable
5875 */
5876 removerecordregions(startloc);
5877 goto again;
5878 }
5879 goto end;
5880 }
5881 if (easy)
5882 goto record;
5883 goto end;
5884 }
5885
5886 if (varlen < 0 && uflag)
5887 varunset(p, var, 0, 0);
5888
5889 if (subtype == VSLENGTH) {
5890 cvtnum(varlen > 0 ? varlen : 0);
5891 goto record;
5892 }
5893
5894 if (subtype == VSNORMAL) {
5895 if (!easy)
5896 goto end;
5897 record:
5898 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5899 goto end;
5900 }
5901
5902#if DEBUG
5903 switch (subtype) {
5904 case VSTRIMLEFT:
5905 case VSTRIMLEFTMAX:
5906 case VSTRIMRIGHT:
5907 case VSTRIMRIGHTMAX:
5908 break;
5909 default:
5910 abort();
5911 }
5912#endif
5913
5914 if (varlen >= 0) {
5915 /*
5916 * Terminate the string and start recording the pattern
5917 * right after it
5918 */
5919 STPUTC('\0', expdest);
5920 patloc = expdest - (char *)stackblock();
5921 if (subevalvar(p, NULL, patloc, subtype,
5922 startloc, varflags, quotes) == 0) {
5923 int amount = expdest - (
5924 (char *)stackblock() + patloc - 1
5925 );
5926 STADJUST(-amount, expdest);
5927 }
5928 /* Remove any recorded regions beyond start of variable */
5929 removerecordregions(startloc);
5930 goto record;
5931 }
5932
5933 end:
5934 if (subtype != VSNORMAL) { /* skip to end of alternative */
5935 int nesting = 1;
5936 for (;;) {
5937 c = *p++;
5938 if (c == CTLESC)
5939 p++;
5940 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5941 if (varlen >= 0)
5942 argbackq = argbackq->next;
5943 } else if (c == CTLVAR) {
5944 if ((*p++ & VSTYPE) != VSNORMAL)
5945 nesting++;
5946 } else if (c == CTLENDVAR) {
5947 if (--nesting == 0)
5948 break;
5949 }
5950 }
5951 }
5952 return p;
5953}
5954
5955/*
5956 * Break the argument string into pieces based upon IFS and add the
5957 * strings to the argument list. The regions of the string to be
5958 * searched for IFS characters have been stored by recordregion.
5959 */
5960static void
5961ifsbreakup(char *string, struct arglist *arglist)
5962{
5963 struct ifsregion *ifsp;
5964 struct strlist *sp;
5965 char *start;
5966 char *p;
5967 char *q;
5968 const char *ifs, *realifs;
5969 int ifsspc;
5970 int nulonly;
5971
5972 start = string;
5973 if (ifslastp != NULL) {
5974 ifsspc = 0;
5975 nulonly = 0;
5976 realifs = ifsset() ? ifsval() : defifs;
5977 ifsp = &ifsfirst;
5978 do {
5979 p = string + ifsp->begoff;
5980 nulonly = ifsp->nulonly;
5981 ifs = nulonly ? nullstr : realifs;
5982 ifsspc = 0;
5983 while (p < string + ifsp->endoff) {
5984 q = p;
5985 if (*p == CTLESC)
5986 p++;
5987 if (!strchr(ifs, *p)) {
5988 p++;
5989 continue;
5990 }
5991 if (!nulonly)
5992 ifsspc = (strchr(defifs, *p) != NULL);
5993 /* Ignore IFS whitespace at start */
5994 if (q == start && ifsspc) {
5995 p++;
5996 start = p;
5997 continue;
5998 }
5999 *q = '\0';
6000 sp = stalloc(sizeof(*sp));
6001 sp->text = start;
6002 *arglist->lastp = sp;
6003 arglist->lastp = &sp->next;
6004 p++;
6005 if (!nulonly) {
6006 for (;;) {
6007 if (p >= string + ifsp->endoff) {
6008 break;
6009 }
6010 q = p;
6011 if (*p == CTLESC)
6012 p++;
6013 if (strchr(ifs, *p) == NULL ) {
6014 p = q;
6015 break;
6016 } else if (strchr(defifs, *p) == NULL) {
6017 if (ifsspc) {
6018 p++;
6019 ifsspc = 0;
6020 } else {
6021 p = q;
6022 break;
6023 }
6024 } else
6025 p++;
6026 }
6027 }
6028 start = p;
6029 } /* while */
6030 ifsp = ifsp->next;
6031 } while (ifsp != NULL);
6032 if (nulonly)
6033 goto add;
6034 }
6035
6036 if (!*start)
6037 return;
6038
6039 add:
6040 sp = stalloc(sizeof(*sp));
6041 sp->text = start;
6042 *arglist->lastp = sp;
6043 arglist->lastp = &sp->next;
6044}
6045
6046static void
6047ifsfree(void)
6048{
6049 struct ifsregion *p;
6050
6051 INT_OFF;
6052 p = ifsfirst.next;
6053 do {
6054 struct ifsregion *ifsp;
6055 ifsp = p->next;
6056 free(p);
6057 p = ifsp;
6058 } while (p);
6059 ifslastp = NULL;
6060 ifsfirst.next = NULL;
6061 INT_ON;
6062}
6063
6064/*
6065 * Add a file name to the list.
6066 */
6067static void
6068addfname(const char *name)
6069{
6070 struct strlist *sp;
6071
6072 sp = stalloc(sizeof(*sp));
6073 sp->text = ststrdup(name);
6074 *exparg.lastp = sp;
6075 exparg.lastp = &sp->next;
6076}
6077
6078static char *expdir;
6079
6080/*
6081 * Do metacharacter (i.e. *, ?, [...]) expansion.
6082 */
6083static void
6084expmeta(char *enddir, char *name)
6085{
6086 char *p;
6087 const char *cp;
6088 char *start;
6089 char *endname;
6090 int metaflag;
6091 struct stat statb;
6092 DIR *dirp;
6093 struct dirent *dp;
6094 int atend;
6095 int matchdot;
6096
6097 metaflag = 0;
6098 start = name;
6099 for (p = name; *p; p++) {
6100 if (*p == '*' || *p == '?')
6101 metaflag = 1;
6102 else if (*p == '[') {
6103 char *q = p + 1;
6104 if (*q == '!')
6105 q++;
6106 for (;;) {
6107 if (*q == '\\')
6108 q++;
6109 if (*q == '/' || *q == '\0')
6110 break;
6111 if (*++q == ']') {
6112 metaflag = 1;
6113 break;
6114 }
6115 }
6116 } else if (*p == '\\')
6117 p++;
6118 else if (*p == '/') {
6119 if (metaflag)
6120 goto out;
6121 start = p + 1;
6122 }
6123 }
6124 out:
6125 if (metaflag == 0) { /* we've reached the end of the file name */
6126 if (enddir != expdir)
6127 metaflag++;
6128 p = name;
6129 do {
6130 if (*p == '\\')
6131 p++;
6132 *enddir++ = *p;
6133 } while (*p++);
6134 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6135 addfname(expdir);
6136 return;
6137 }
6138 endname = p;
6139 if (name < start) {
6140 p = name;
6141 do {
6142 if (*p == '\\')
6143 p++;
6144 *enddir++ = *p++;
6145 } while (p < start);
6146 }
6147 if (enddir == expdir) {
6148 cp = ".";
6149 } else if (enddir == expdir + 1 && *expdir == '/') {
6150 cp = "/";
6151 } else {
6152 cp = expdir;
6153 enddir[-1] = '\0';
6154 }
6155 dirp = opendir(cp);
6156 if (dirp == NULL)
6157 return;
6158 if (enddir != expdir)
6159 enddir[-1] = '/';
6160 if (*endname == 0) {
6161 atend = 1;
6162 } else {
6163 atend = 0;
6164 *endname++ = '\0';
6165 }
6166 matchdot = 0;
6167 p = start;
6168 if (*p == '\\')
6169 p++;
6170 if (*p == '.')
6171 matchdot++;
6172 while (! intpending && (dp = readdir(dirp)) != NULL) {
6173 if (dp->d_name[0] == '.' && ! matchdot)
6174 continue;
6175 if (pmatch(start, dp->d_name)) {
6176 if (atend) {
6177 strcpy(enddir, dp->d_name);
6178 addfname(expdir);
6179 } else {
6180 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6181 continue;
6182 p[-1] = '/';
6183 expmeta(p, endname);
6184 }
6185 }
6186 }
6187 closedir(dirp);
6188 if (! atend)
6189 endname[-1] = '/';
6190}
6191
6192static struct strlist *
6193msort(struct strlist *list, int len)
6194{
6195 struct strlist *p, *q = NULL;
6196 struct strlist **lpp;
6197 int half;
6198 int n;
6199
6200 if (len <= 1)
6201 return list;
6202 half = len >> 1;
6203 p = list;
6204 for (n = half; --n >= 0; ) {
6205 q = p;
6206 p = p->next;
6207 }
6208 q->next = NULL; /* terminate first half of list */
6209 q = msort(list, half); /* sort first half of list */
6210 p = msort(p, len - half); /* sort second half */
6211 lpp = &list;
6212 for (;;) {
6213#if ENABLE_LOCALE_SUPPORT
6214 if (strcoll(p->text, q->text) < 0)
6215#else
6216 if (strcmp(p->text, q->text) < 0)
6217#endif
6218 {
6219 *lpp = p;
6220 lpp = &p->next;
6221 p = *lpp;
6222 if (p == NULL) {
6223 *lpp = q;
6224 break;
6225 }
6226 } else {
6227 *lpp = q;
6228 lpp = &q->next;
6229 q = *lpp;
6230 if (q == NULL) {
6231 *lpp = p;
6232 break;
6233 }
6234 }
6235 }
6236 return list;
6237}
6238
6239/*
6240 * Sort the results of file name expansion. It calculates the number of
6241 * strings to sort and then calls msort (short for merge sort) to do the
6242 * work.
6243 */
6244static struct strlist *
6245expsort(struct strlist *str)
6246{
6247 int len;
6248 struct strlist *sp;
6249
6250 len = 0;
6251 for (sp = str; sp; sp = sp->next)
6252 len++;
6253 return msort(str, len);
6254}
6255
6256static void
6257expandmeta(struct strlist *str, int flag)
6258{
6259 static const char metachars[] = {
6260 '*', '?', '[', 0
6261 };
6262 /* TODO - EXP_REDIR */
6263
6264 while (str) {
6265 struct strlist **savelastp;
6266 struct strlist *sp;
6267 char *p;
6268
6269 if (fflag)
6270 goto nometa;
6271 if (!strpbrk(str->text, metachars))
6272 goto nometa;
6273 savelastp = exparg.lastp;
6274
6275 INT_OFF;
6276 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6277 {
6278 int i = strlen(str->text);
6279 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6280 }
6281
6282 expmeta(expdir, p);
6283 free(expdir);
6284 if (p != str->text)
6285 free(p);
6286 INT_ON;
6287 if (exparg.lastp == savelastp) {
6288 /*
6289 * no matches
6290 */
6291 nometa:
6292 *exparg.lastp = str;
6293 rmescapes(str->text);
6294 exparg.lastp = &str->next;
6295 } else {
6296 *exparg.lastp = NULL;
6297 *savelastp = sp = expsort(*savelastp);
6298 while (sp->next != NULL)
6299 sp = sp->next;
6300 exparg.lastp = &sp->next;
6301 }
6302 str = str->next;
6303 }
6304}
6305
6306/*
6307 * Perform variable substitution and command substitution on an argument,
6308 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6309 * perform splitting and file name expansion. When arglist is NULL, perform
6310 * here document expansion.
6311 */
6312static void
6313expandarg(union node *arg, struct arglist *arglist, int flag)
6314{
6315 struct strlist *sp;
6316 char *p;
6317
6318 argbackq = arg->narg.backquote;
6319 STARTSTACKSTR(expdest);
6320 ifsfirst.next = NULL;
6321 ifslastp = NULL;
6322 argstr(arg->narg.text, flag);
6323 p = _STPUTC('\0', expdest);
6324 expdest = p - 1;
6325 if (arglist == NULL) {
6326 return; /* here document expanded */
6327 }
6328 p = grabstackstr(p);
6329 exparg.lastp = &exparg.list;
6330 /*
6331 * TODO - EXP_REDIR
6332 */
6333 if (flag & EXP_FULL) {
6334 ifsbreakup(p, &exparg);
6335 *exparg.lastp = NULL;
6336 exparg.lastp = &exparg.list;
6337 expandmeta(exparg.list, flag);
6338 } else {
6339 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6340 rmescapes(p);
6341 sp = stalloc(sizeof(*sp));
6342 sp->text = p;
6343 *exparg.lastp = sp;
6344 exparg.lastp = &sp->next;
6345 }
6346 if (ifsfirst.next)
6347 ifsfree();
6348 *exparg.lastp = NULL;
6349 if (exparg.list) {
6350 *arglist->lastp = exparg.list;
6351 arglist->lastp = exparg.lastp;
6352 }
6353}
6354
6355/*
6356 * Expand shell variables and backquotes inside a here document.
6357 */
6358static void
6359expandhere(union node *arg, int fd)
6360{
6361 herefd = fd;
6362 expandarg(arg, (struct arglist *)NULL, 0);
6363 full_write(fd, stackblock(), expdest - (char *)stackblock());
6364}
6365
6366/*
6367 * Returns true if the pattern matches the string.
6368 */
6369static int
6370patmatch(char *pattern, const char *string)
6371{
6372 return pmatch(preglob(pattern, 0, 0), string);
6373}
6374
6375/*
6376 * See if a pattern matches in a case statement.
6377 */
6378static int
6379casematch(union node *pattern, char *val)
6380{
6381 struct stackmark smark;
6382 int result;
6383
6384 setstackmark(&smark);
6385 argbackq = pattern->narg.backquote;
6386 STARTSTACKSTR(expdest);
6387 ifslastp = NULL;
6388 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6389 STACKSTRNUL(expdest);
6390 result = patmatch(stackblock(), val);
6391 popstackmark(&smark);
6392 return result;
6393}
6394
6395
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006396/* ============ find_command */
6397
6398struct builtincmd {
6399 const char *name;
6400 int (*builtin)(int, char **);
6401 /* unsigned flags; */
6402};
6403#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6404#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6405#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6406
6407struct cmdentry {
6408 int cmdtype;
6409 union param {
6410 int index;
6411 const struct builtincmd *cmd;
6412 struct funcnode *func;
6413 } u;
6414};
6415/* values of cmdtype */
6416#define CMDUNKNOWN -1 /* no entry in table for command */
6417#define CMDNORMAL 0 /* command is an executable program */
6418#define CMDFUNCTION 1 /* command is a shell function */
6419#define CMDBUILTIN 2 /* command is a shell builtin */
6420
6421/* action to find_command() */
6422#define DO_ERR 0x01 /* prints errors */
6423#define DO_ABS 0x02 /* checks absolute paths */
6424#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6425#define DO_ALTPATH 0x08 /* using alternate path */
6426#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6427
6428static void find_command(char *, struct cmdentry *, int, const char *);
6429
6430
6431/* ============ Hashing commands */
6432
6433/*
6434 * When commands are first encountered, they are entered in a hash table.
6435 * This ensures that a full path search will not have to be done for them
6436 * on each invocation.
6437 *
6438 * We should investigate converting to a linear search, even though that
6439 * would make the command name "hash" a misnomer.
6440 */
6441
6442#define CMDTABLESIZE 31 /* should be prime */
6443#define ARB 1 /* actual size determined at run time */
6444
6445struct tblentry {
6446 struct tblentry *next; /* next entry in hash chain */
6447 union param param; /* definition of builtin function */
6448 short cmdtype; /* index identifying command */
6449 char rehash; /* if set, cd done since entry created */
6450 char cmdname[ARB]; /* name of command */
6451};
6452
6453static struct tblentry *cmdtable[CMDTABLESIZE];
6454static int builtinloc = -1; /* index in path of %builtin, or -1 */
6455
6456static void
6457tryexec(char *cmd, char **argv, char **envp)
6458{
6459 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006460
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006461#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006462 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006463 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006464
6465 a = find_applet_by_name(cmd);
6466 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006467 if (a->noexec) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006468 current_applet = a;
Denis Vlasenkof5294e12007-04-14 10:09:57 +00006469 run_current_applet_and_exit(argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006470 }
6471 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006472 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006473 /* If they called chroot or otherwise made the binary no longer
6474 * executable, fall through */
6475 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006476 }
6477#endif
6478
6479 repeat:
6480#ifdef SYSV
6481 do {
6482 execve(cmd, argv, envp);
6483 } while (errno == EINTR);
6484#else
6485 execve(cmd, argv, envp);
6486#endif
6487 if (repeated++) {
6488 free(argv);
6489 } else if (errno == ENOEXEC) {
6490 char **ap;
6491 char **new;
6492
6493 for (ap = argv; *ap; ap++)
6494 ;
6495 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6496 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006497 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006498 ap += 2;
6499 argv++;
6500 while ((*ap++ = *argv++))
6501 ;
6502 argv = new;
6503 goto repeat;
6504 }
6505}
6506
6507/*
6508 * Exec a program. Never returns. If you change this routine, you may
6509 * have to change the find_command routine as well.
6510 */
6511#define environment() listvars(VEXPORT, VUNSET, 0)
6512static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6513static void
6514shellexec(char **argv, const char *path, int idx)
6515{
6516 char *cmdname;
6517 int e;
6518 char **envp;
6519 int exerrno;
6520
6521 clearredir(1);
6522 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006523 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006524#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006525 || find_applet_by_name(argv[0])
6526#endif
6527 ) {
6528 tryexec(argv[0], argv, envp);
6529 e = errno;
6530 } else {
6531 e = ENOENT;
6532 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6533 if (--idx < 0 && pathopt == NULL) {
6534 tryexec(cmdname, argv, envp);
6535 if (errno != ENOENT && errno != ENOTDIR)
6536 e = errno;
6537 }
6538 stunalloc(cmdname);
6539 }
6540 }
6541
6542 /* Map to POSIX errors */
6543 switch (e) {
6544 case EACCES:
6545 exerrno = 126;
6546 break;
6547 case ENOENT:
6548 exerrno = 127;
6549 break;
6550 default:
6551 exerrno = 2;
6552 break;
6553 }
6554 exitstatus = exerrno;
6555 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6556 argv[0], e, suppressint ));
6557 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6558 /* NOTREACHED */
6559}
6560
6561static void
6562printentry(struct tblentry *cmdp)
6563{
6564 int idx;
6565 const char *path;
6566 char *name;
6567
6568 idx = cmdp->param.index;
6569 path = pathval();
6570 do {
6571 name = padvance(&path, cmdp->cmdname);
6572 stunalloc(name);
6573 } while (--idx >= 0);
6574 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6575}
6576
6577/*
6578 * Clear out command entries. The argument specifies the first entry in
6579 * PATH which has changed.
6580 */
6581static void
6582clearcmdentry(int firstchange)
6583{
6584 struct tblentry **tblp;
6585 struct tblentry **pp;
6586 struct tblentry *cmdp;
6587
6588 INT_OFF;
6589 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6590 pp = tblp;
6591 while ((cmdp = *pp) != NULL) {
6592 if ((cmdp->cmdtype == CMDNORMAL &&
6593 cmdp->param.index >= firstchange)
6594 || (cmdp->cmdtype == CMDBUILTIN &&
6595 builtinloc >= firstchange)
6596 ) {
6597 *pp = cmdp->next;
6598 free(cmdp);
6599 } else {
6600 pp = &cmdp->next;
6601 }
6602 }
6603 }
6604 INT_ON;
6605}
6606
6607/*
6608 * Locate a command in the command hash table. If "add" is nonzero,
6609 * add the command to the table if it is not already present. The
6610 * variable "lastcmdentry" is set to point to the address of the link
6611 * pointing to the entry, so that delete_cmd_entry can delete the
6612 * entry.
6613 *
6614 * Interrupts must be off if called with add != 0.
6615 */
6616static struct tblentry **lastcmdentry;
6617
6618static struct tblentry *
6619cmdlookup(const char *name, int add)
6620{
6621 unsigned int hashval;
6622 const char *p;
6623 struct tblentry *cmdp;
6624 struct tblentry **pp;
6625
6626 p = name;
6627 hashval = (unsigned char)*p << 4;
6628 while (*p)
6629 hashval += (unsigned char)*p++;
6630 hashval &= 0x7FFF;
6631 pp = &cmdtable[hashval % CMDTABLESIZE];
6632 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6633 if (strcmp(cmdp->cmdname, name) == 0)
6634 break;
6635 pp = &cmdp->next;
6636 }
6637 if (add && cmdp == NULL) {
6638 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6639 + strlen(name) + 1);
6640 cmdp->next = NULL;
6641 cmdp->cmdtype = CMDUNKNOWN;
6642 strcpy(cmdp->cmdname, name);
6643 }
6644 lastcmdentry = pp;
6645 return cmdp;
6646}
6647
6648/*
6649 * Delete the command entry returned on the last lookup.
6650 */
6651static void
6652delete_cmd_entry(void)
6653{
6654 struct tblentry *cmdp;
6655
6656 INT_OFF;
6657 cmdp = *lastcmdentry;
6658 *lastcmdentry = cmdp->next;
6659 if (cmdp->cmdtype == CMDFUNCTION)
6660 freefunc(cmdp->param.func);
6661 free(cmdp);
6662 INT_ON;
6663}
6664
6665/*
6666 * Add a new command entry, replacing any existing command entry for
6667 * the same name - except special builtins.
6668 */
6669static void
6670addcmdentry(char *name, struct cmdentry *entry)
6671{
6672 struct tblentry *cmdp;
6673
6674 cmdp = cmdlookup(name, 1);
6675 if (cmdp->cmdtype == CMDFUNCTION) {
6676 freefunc(cmdp->param.func);
6677 }
6678 cmdp->cmdtype = entry->cmdtype;
6679 cmdp->param = entry->u;
6680 cmdp->rehash = 0;
6681}
6682
6683static int
6684hashcmd(int argc, char **argv)
6685{
6686 struct tblentry **pp;
6687 struct tblentry *cmdp;
6688 int c;
6689 struct cmdentry entry;
6690 char *name;
6691
6692 while ((c = nextopt("r")) != '\0') {
6693 clearcmdentry(0);
6694 return 0;
6695 }
6696 if (*argptr == NULL) {
6697 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6698 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6699 if (cmdp->cmdtype == CMDNORMAL)
6700 printentry(cmdp);
6701 }
6702 }
6703 return 0;
6704 }
6705 c = 0;
6706 while ((name = *argptr) != NULL) {
6707 cmdp = cmdlookup(name, 0);
6708 if (cmdp != NULL
6709 && (cmdp->cmdtype == CMDNORMAL
6710 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6711 delete_cmd_entry();
6712 find_command(name, &entry, DO_ERR, pathval());
6713 if (entry.cmdtype == CMDUNKNOWN)
6714 c = 1;
6715 argptr++;
6716 }
6717 return c;
6718}
6719
6720/*
6721 * Called when a cd is done. Marks all commands so the next time they
6722 * are executed they will be rehashed.
6723 */
6724static void
6725hashcd(void)
6726{
6727 struct tblentry **pp;
6728 struct tblentry *cmdp;
6729
6730 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6731 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6732 if (cmdp->cmdtype == CMDNORMAL || (
6733 cmdp->cmdtype == CMDBUILTIN &&
6734 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6735 builtinloc > 0
6736 ))
6737 cmdp->rehash = 1;
6738 }
6739 }
6740}
6741
6742/*
6743 * Fix command hash table when PATH changed.
6744 * Called before PATH is changed. The argument is the new value of PATH;
6745 * pathval() still returns the old value at this point.
6746 * Called with interrupts off.
6747 */
6748static void
6749changepath(const char *newval)
6750{
6751 const char *old, *new;
6752 int idx;
6753 int firstchange;
6754 int idx_bltin;
6755
6756 old = pathval();
6757 new = newval;
6758 firstchange = 9999; /* assume no change */
6759 idx = 0;
6760 idx_bltin = -1;
6761 for (;;) {
6762 if (*old != *new) {
6763 firstchange = idx;
6764 if ((*old == '\0' && *new == ':')
6765 || (*old == ':' && *new == '\0'))
6766 firstchange++;
6767 old = new; /* ignore subsequent differences */
6768 }
6769 if (*new == '\0')
6770 break;
6771 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6772 idx_bltin = idx;
6773 if (*new == ':') {
6774 idx++;
6775 }
6776 new++, old++;
6777 }
6778 if (builtinloc < 0 && idx_bltin >= 0)
6779 builtinloc = idx_bltin; /* zap builtins */
6780 if (builtinloc >= 0 && idx_bltin < 0)
6781 firstchange = 0;
6782 clearcmdentry(firstchange);
6783 builtinloc = idx_bltin;
6784}
6785
6786#define TEOF 0
6787#define TNL 1
6788#define TREDIR 2
6789#define TWORD 3
6790#define TSEMI 4
6791#define TBACKGND 5
6792#define TAND 6
6793#define TOR 7
6794#define TPIPE 8
6795#define TLP 9
6796#define TRP 10
6797#define TENDCASE 11
6798#define TENDBQUOTE 12
6799#define TNOT 13
6800#define TCASE 14
6801#define TDO 15
6802#define TDONE 16
6803#define TELIF 17
6804#define TELSE 18
6805#define TESAC 19
6806#define TFI 20
6807#define TFOR 21
6808#define TIF 22
6809#define TIN 23
6810#define TTHEN 24
6811#define TUNTIL 25
6812#define TWHILE 26
6813#define TBEGIN 27
6814#define TEND 28
6815
6816/* first char is indicating which tokens mark the end of a list */
6817static const char *const tokname_array[] = {
6818 "\1end of file",
6819 "\0newline",
6820 "\0redirection",
6821 "\0word",
6822 "\0;",
6823 "\0&",
6824 "\0&&",
6825 "\0||",
6826 "\0|",
6827 "\0(",
6828 "\1)",
6829 "\1;;",
6830 "\1`",
6831#define KWDOFFSET 13
6832 /* the following are keywords */
6833 "\0!",
6834 "\0case",
6835 "\1do",
6836 "\1done",
6837 "\1elif",
6838 "\1else",
6839 "\1esac",
6840 "\1fi",
6841 "\0for",
6842 "\0if",
6843 "\0in",
6844 "\1then",
6845 "\0until",
6846 "\0while",
6847 "\0{",
6848 "\1}",
6849};
6850
6851static const char *
6852tokname(int tok)
6853{
6854 static char buf[16];
6855
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006856//try this:
6857//if (tok < TSEMI) return tokname_array[tok] + 1;
6858//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6859//return buf;
6860
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006861 if (tok >= TSEMI)
6862 buf[0] = '"';
6863 sprintf(buf + (tok >= TSEMI), "%s%c",
6864 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6865 return buf;
6866}
6867
6868/* Wrapper around strcmp for qsort/bsearch/... */
6869static int
6870pstrcmp(const void *a, const void *b)
6871{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006872 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006873}
6874
6875static const char *const *
6876findkwd(const char *s)
6877{
6878 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006879 (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
6880 sizeof(char *), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006881}
6882
6883/*
6884 * Locate and print what a word is...
6885 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006886static int
6887describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006888{
6889 struct cmdentry entry;
6890 struct tblentry *cmdp;
6891#if ENABLE_ASH_ALIAS
6892 const struct alias *ap;
6893#endif
6894 const char *path = pathval();
6895
6896 if (describe_command_verbose) {
6897 out1str(command);
6898 }
6899
6900 /* First look at the keywords */
6901 if (findkwd(command)) {
6902 out1str(describe_command_verbose ? " is a shell keyword" : command);
6903 goto out;
6904 }
6905
6906#if ENABLE_ASH_ALIAS
6907 /* Then look at the aliases */
6908 ap = lookupalias(command, 0);
6909 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00006910 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006911 out1str("alias ");
6912 printalias(ap);
6913 return 0;
6914 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00006915 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006916 goto out;
6917 }
6918#endif
6919 /* Then check if it is a tracked alias */
6920 cmdp = cmdlookup(command, 0);
6921 if (cmdp != NULL) {
6922 entry.cmdtype = cmdp->cmdtype;
6923 entry.u = cmdp->param;
6924 } else {
6925 /* Finally use brute force */
6926 find_command(command, &entry, DO_ABS, path);
6927 }
6928
6929 switch (entry.cmdtype) {
6930 case CMDNORMAL: {
6931 int j = entry.u.index;
6932 char *p;
6933 if (j == -1) {
6934 p = command;
6935 } else {
6936 do {
6937 p = padvance(&path, command);
6938 stunalloc(p);
6939 } while (--j >= 0);
6940 }
6941 if (describe_command_verbose) {
6942 out1fmt(" is%s %s",
6943 (cmdp ? " a tracked alias for" : nullstr), p
6944 );
6945 } else {
6946 out1str(p);
6947 }
6948 break;
6949 }
6950
6951 case CMDFUNCTION:
6952 if (describe_command_verbose) {
6953 out1str(" is a shell function");
6954 } else {
6955 out1str(command);
6956 }
6957 break;
6958
6959 case CMDBUILTIN:
6960 if (describe_command_verbose) {
6961 out1fmt(" is a %sshell builtin",
6962 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
6963 "special " : nullstr
6964 );
6965 } else {
6966 out1str(command);
6967 }
6968 break;
6969
6970 default:
6971 if (describe_command_verbose) {
6972 out1str(": not found\n");
6973 }
6974 return 127;
6975 }
6976 out:
6977 outstr("\n", stdout);
6978 return 0;
6979}
6980
6981static int
6982typecmd(int argc, char **argv)
6983{
Denis Vlasenko46846e22007-05-20 13:08:31 +00006984 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006985 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00006986 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006987
Denis Vlasenko46846e22007-05-20 13:08:31 +00006988 /* type -p ... ? (we don't bother checking for 'p') */
6989 if (argv[1][0] == '-') {
6990 i++;
6991 verbose = 0;
6992 }
6993 while (i < argc) {
6994 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006995 }
6996 return err;
6997}
6998
6999#if ENABLE_ASH_CMDCMD
7000static int
7001commandcmd(int argc, char **argv)
7002{
7003 int c;
7004 enum {
7005 VERIFY_BRIEF = 1,
7006 VERIFY_VERBOSE = 2,
7007 } verify = 0;
7008
7009 while ((c = nextopt("pvV")) != '\0')
7010 if (c == 'V')
7011 verify |= VERIFY_VERBOSE;
7012 else if (c == 'v')
7013 verify |= VERIFY_BRIEF;
7014#if DEBUG
7015 else if (c != 'p')
7016 abort();
7017#endif
7018 if (verify)
7019 return describe_command(*argptr, verify - VERIFY_BRIEF);
7020
7021 return 0;
7022}
7023#endif
7024
7025
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007026/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007027
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007028static int funcblocksize; /* size of structures in function */
7029static int funcstringsize; /* size of strings in node */
7030static void *funcblock; /* block to allocate function from */
7031static char *funcstring; /* block to allocate strings from */
7032
Eric Andersencb57d552001-06-28 07:25:16 +00007033/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007034#define EV_EXIT 01 /* exit after evaluating tree */
7035#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7036#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007037
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007038static const short nodesize[26] = {
7039 SHELL_ALIGN(sizeof(struct ncmd)),
7040 SHELL_ALIGN(sizeof(struct npipe)),
7041 SHELL_ALIGN(sizeof(struct nredir)),
7042 SHELL_ALIGN(sizeof(struct nredir)),
7043 SHELL_ALIGN(sizeof(struct nredir)),
7044 SHELL_ALIGN(sizeof(struct nbinary)),
7045 SHELL_ALIGN(sizeof(struct nbinary)),
7046 SHELL_ALIGN(sizeof(struct nbinary)),
7047 SHELL_ALIGN(sizeof(struct nif)),
7048 SHELL_ALIGN(sizeof(struct nbinary)),
7049 SHELL_ALIGN(sizeof(struct nbinary)),
7050 SHELL_ALIGN(sizeof(struct nfor)),
7051 SHELL_ALIGN(sizeof(struct ncase)),
7052 SHELL_ALIGN(sizeof(struct nclist)),
7053 SHELL_ALIGN(sizeof(struct narg)),
7054 SHELL_ALIGN(sizeof(struct narg)),
7055 SHELL_ALIGN(sizeof(struct nfile)),
7056 SHELL_ALIGN(sizeof(struct nfile)),
7057 SHELL_ALIGN(sizeof(struct nfile)),
7058 SHELL_ALIGN(sizeof(struct nfile)),
7059 SHELL_ALIGN(sizeof(struct nfile)),
7060 SHELL_ALIGN(sizeof(struct ndup)),
7061 SHELL_ALIGN(sizeof(struct ndup)),
7062 SHELL_ALIGN(sizeof(struct nhere)),
7063 SHELL_ALIGN(sizeof(struct nhere)),
7064 SHELL_ALIGN(sizeof(struct nnot)),
7065};
7066
7067static void calcsize(union node *n);
7068
7069static void
7070sizenodelist(struct nodelist *lp)
7071{
7072 while (lp) {
7073 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7074 calcsize(lp->n);
7075 lp = lp->next;
7076 }
7077}
7078
7079static void
7080calcsize(union node *n)
7081{
7082 if (n == NULL)
7083 return;
7084 funcblocksize += nodesize[n->type];
7085 switch (n->type) {
7086 case NCMD:
7087 calcsize(n->ncmd.redirect);
7088 calcsize(n->ncmd.args);
7089 calcsize(n->ncmd.assign);
7090 break;
7091 case NPIPE:
7092 sizenodelist(n->npipe.cmdlist);
7093 break;
7094 case NREDIR:
7095 case NBACKGND:
7096 case NSUBSHELL:
7097 calcsize(n->nredir.redirect);
7098 calcsize(n->nredir.n);
7099 break;
7100 case NAND:
7101 case NOR:
7102 case NSEMI:
7103 case NWHILE:
7104 case NUNTIL:
7105 calcsize(n->nbinary.ch2);
7106 calcsize(n->nbinary.ch1);
7107 break;
7108 case NIF:
7109 calcsize(n->nif.elsepart);
7110 calcsize(n->nif.ifpart);
7111 calcsize(n->nif.test);
7112 break;
7113 case NFOR:
7114 funcstringsize += strlen(n->nfor.var) + 1;
7115 calcsize(n->nfor.body);
7116 calcsize(n->nfor.args);
7117 break;
7118 case NCASE:
7119 calcsize(n->ncase.cases);
7120 calcsize(n->ncase.expr);
7121 break;
7122 case NCLIST:
7123 calcsize(n->nclist.body);
7124 calcsize(n->nclist.pattern);
7125 calcsize(n->nclist.next);
7126 break;
7127 case NDEFUN:
7128 case NARG:
7129 sizenodelist(n->narg.backquote);
7130 funcstringsize += strlen(n->narg.text) + 1;
7131 calcsize(n->narg.next);
7132 break;
7133 case NTO:
7134 case NCLOBBER:
7135 case NFROM:
7136 case NFROMTO:
7137 case NAPPEND:
7138 calcsize(n->nfile.fname);
7139 calcsize(n->nfile.next);
7140 break;
7141 case NTOFD:
7142 case NFROMFD:
7143 calcsize(n->ndup.vname);
7144 calcsize(n->ndup.next);
7145 break;
7146 case NHERE:
7147 case NXHERE:
7148 calcsize(n->nhere.doc);
7149 calcsize(n->nhere.next);
7150 break;
7151 case NNOT:
7152 calcsize(n->nnot.com);
7153 break;
7154 };
7155}
7156
7157static char *
7158nodeckstrdup(char *s)
7159{
7160 char *rtn = funcstring;
7161
7162 strcpy(funcstring, s);
7163 funcstring += strlen(s) + 1;
7164 return rtn;
7165}
7166
7167static union node *copynode(union node *);
7168
7169static struct nodelist *
7170copynodelist(struct nodelist *lp)
7171{
7172 struct nodelist *start;
7173 struct nodelist **lpp;
7174
7175 lpp = &start;
7176 while (lp) {
7177 *lpp = funcblock;
7178 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7179 (*lpp)->n = copynode(lp->n);
7180 lp = lp->next;
7181 lpp = &(*lpp)->next;
7182 }
7183 *lpp = NULL;
7184 return start;
7185}
7186
7187static union node *
7188copynode(union node *n)
7189{
7190 union node *new;
7191
7192 if (n == NULL)
7193 return NULL;
7194 new = funcblock;
7195 funcblock = (char *) funcblock + nodesize[n->type];
7196
7197 switch (n->type) {
7198 case NCMD:
7199 new->ncmd.redirect = copynode(n->ncmd.redirect);
7200 new->ncmd.args = copynode(n->ncmd.args);
7201 new->ncmd.assign = copynode(n->ncmd.assign);
7202 break;
7203 case NPIPE:
7204 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7205 new->npipe.backgnd = n->npipe.backgnd;
7206 break;
7207 case NREDIR:
7208 case NBACKGND:
7209 case NSUBSHELL:
7210 new->nredir.redirect = copynode(n->nredir.redirect);
7211 new->nredir.n = copynode(n->nredir.n);
7212 break;
7213 case NAND:
7214 case NOR:
7215 case NSEMI:
7216 case NWHILE:
7217 case NUNTIL:
7218 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7219 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7220 break;
7221 case NIF:
7222 new->nif.elsepart = copynode(n->nif.elsepart);
7223 new->nif.ifpart = copynode(n->nif.ifpart);
7224 new->nif.test = copynode(n->nif.test);
7225 break;
7226 case NFOR:
7227 new->nfor.var = nodeckstrdup(n->nfor.var);
7228 new->nfor.body = copynode(n->nfor.body);
7229 new->nfor.args = copynode(n->nfor.args);
7230 break;
7231 case NCASE:
7232 new->ncase.cases = copynode(n->ncase.cases);
7233 new->ncase.expr = copynode(n->ncase.expr);
7234 break;
7235 case NCLIST:
7236 new->nclist.body = copynode(n->nclist.body);
7237 new->nclist.pattern = copynode(n->nclist.pattern);
7238 new->nclist.next = copynode(n->nclist.next);
7239 break;
7240 case NDEFUN:
7241 case NARG:
7242 new->narg.backquote = copynodelist(n->narg.backquote);
7243 new->narg.text = nodeckstrdup(n->narg.text);
7244 new->narg.next = copynode(n->narg.next);
7245 break;
7246 case NTO:
7247 case NCLOBBER:
7248 case NFROM:
7249 case NFROMTO:
7250 case NAPPEND:
7251 new->nfile.fname = copynode(n->nfile.fname);
7252 new->nfile.fd = n->nfile.fd;
7253 new->nfile.next = copynode(n->nfile.next);
7254 break;
7255 case NTOFD:
7256 case NFROMFD:
7257 new->ndup.vname = copynode(n->ndup.vname);
7258 new->ndup.dupfd = n->ndup.dupfd;
7259 new->ndup.fd = n->ndup.fd;
7260 new->ndup.next = copynode(n->ndup.next);
7261 break;
7262 case NHERE:
7263 case NXHERE:
7264 new->nhere.doc = copynode(n->nhere.doc);
7265 new->nhere.fd = n->nhere.fd;
7266 new->nhere.next = copynode(n->nhere.next);
7267 break;
7268 case NNOT:
7269 new->nnot.com = copynode(n->nnot.com);
7270 break;
7271 };
7272 new->type = n->type;
7273 return new;
7274}
7275
7276/*
7277 * Make a copy of a parse tree.
7278 */
7279static struct funcnode *
7280copyfunc(union node *n)
7281{
7282 struct funcnode *f;
7283 size_t blocksize;
7284
7285 funcblocksize = offsetof(struct funcnode, n);
7286 funcstringsize = 0;
7287 calcsize(n);
7288 blocksize = funcblocksize;
7289 f = ckmalloc(blocksize + funcstringsize);
7290 funcblock = (char *) f + offsetof(struct funcnode, n);
7291 funcstring = (char *) f + blocksize;
7292 copynode(n);
7293 f->count = 0;
7294 return f;
7295}
7296
7297/*
7298 * Define a shell function.
7299 */
7300static void
7301defun(char *name, union node *func)
7302{
7303 struct cmdentry entry;
7304
7305 INT_OFF;
7306 entry.cmdtype = CMDFUNCTION;
7307 entry.u.func = copyfunc(func);
7308 addcmdentry(name, &entry);
7309 INT_ON;
7310}
7311
7312static int evalskip; /* set if we are skipping commands */
7313/* reasons for skipping commands (see comment on breakcmd routine) */
7314#define SKIPBREAK (1 << 0)
7315#define SKIPCONT (1 << 1)
7316#define SKIPFUNC (1 << 2)
7317#define SKIPFILE (1 << 3)
7318#define SKIPEVAL (1 << 4)
7319static int skipcount; /* number of levels to skip */
7320static int funcnest; /* depth of function calls */
7321
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007322/* forward decl way out to parsing code - dotrap needs it */
7323static int evalstring(char *s, int mask);
7324
7325/*
7326 * Called to execute a trap. Perhaps we should avoid entering new trap
7327 * handlers while we are executing a trap handler.
7328 */
7329static int
7330dotrap(void)
7331{
7332 char *p;
7333 char *q;
7334 int i;
7335 int savestatus;
7336 int skip = 0;
7337
7338 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007339 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007340 xbarrier();
7341
7342 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7343 if (!*q)
7344 continue;
7345 *q = '\0';
7346
7347 p = trap[i + 1];
7348 if (!p)
7349 continue;
7350 skip = evalstring(p, SKIPEVAL);
7351 exitstatus = savestatus;
7352 if (skip)
7353 break;
7354 }
7355
7356 return skip;
7357}
7358
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007359/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007360static void evalloop(union node *, int);
7361static void evalfor(union node *, int);
7362static void evalcase(union node *, int);
7363static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007364static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007365static void evalpipe(union node *, int);
7366static void evalcommand(union node *, int);
7367static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007368static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007369
Eric Andersen62483552001-07-10 06:09:16 +00007370/*
Eric Andersenc470f442003-07-28 09:56:35 +00007371 * Evaluate a parse tree. The value is left in the global variable
7372 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007373 */
Eric Andersenc470f442003-07-28 09:56:35 +00007374static void
7375evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007376{
Eric Andersenc470f442003-07-28 09:56:35 +00007377 int checkexit = 0;
7378 void (*evalfn)(union node *, int);
7379 unsigned isor;
7380 int status;
7381 if (n == NULL) {
7382 TRACE(("evaltree(NULL) called\n"));
7383 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007384 }
Eric Andersenc470f442003-07-28 09:56:35 +00007385 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007386 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007387 switch (n->type) {
7388 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007389#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007390 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007391 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007392 break;
7393#endif
7394 case NNOT:
7395 evaltree(n->nnot.com, EV_TESTED);
7396 status = !exitstatus;
7397 goto setstatus;
7398 case NREDIR:
7399 expredir(n->nredir.redirect);
7400 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7401 if (!status) {
7402 evaltree(n->nredir.n, flags & EV_TESTED);
7403 status = exitstatus;
7404 }
7405 popredir(0);
7406 goto setstatus;
7407 case NCMD:
7408 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007409 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007410 if (eflag && !(flags & EV_TESTED))
7411 checkexit = ~0;
7412 goto calleval;
7413 case NFOR:
7414 evalfn = evalfor;
7415 goto calleval;
7416 case NWHILE:
7417 case NUNTIL:
7418 evalfn = evalloop;
7419 goto calleval;
7420 case NSUBSHELL:
7421 case NBACKGND:
7422 evalfn = evalsubshell;
7423 goto calleval;
7424 case NPIPE:
7425 evalfn = evalpipe;
7426 goto checkexit;
7427 case NCASE:
7428 evalfn = evalcase;
7429 goto calleval;
7430 case NAND:
7431 case NOR:
7432 case NSEMI:
7433#if NAND + 1 != NOR
7434#error NAND + 1 != NOR
7435#endif
7436#if NOR + 1 != NSEMI
7437#error NOR + 1 != NSEMI
7438#endif
7439 isor = n->type - NAND;
7440 evaltree(
7441 n->nbinary.ch1,
7442 (flags | ((isor >> 1) - 1)) & EV_TESTED
7443 );
7444 if (!exitstatus == isor)
7445 break;
7446 if (!evalskip) {
7447 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007448 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007449 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007450 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007451 evalfn(n, flags);
7452 break;
7453 }
7454 break;
7455 case NIF:
7456 evaltree(n->nif.test, EV_TESTED);
7457 if (evalskip)
7458 break;
7459 if (exitstatus == 0) {
7460 n = n->nif.ifpart;
7461 goto evaln;
7462 } else if (n->nif.elsepart) {
7463 n = n->nif.elsepart;
7464 goto evaln;
7465 }
7466 goto success;
7467 case NDEFUN:
7468 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007469 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007470 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007471 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007472 exitstatus = status;
7473 break;
7474 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007475 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007476 if ((checkexit & exitstatus))
7477 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007478 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007479 goto exexit;
7480
7481 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007482 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007483 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007484 }
Eric Andersen62483552001-07-10 06:09:16 +00007485}
7486
Eric Andersenc470f442003-07-28 09:56:35 +00007487#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7488static
7489#endif
7490void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7491
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007492static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007493
7494static void
7495evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007496{
7497 int status;
7498
7499 loopnest++;
7500 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007501 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007502 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007503 int i;
7504
Eric Andersencb57d552001-06-28 07:25:16 +00007505 evaltree(n->nbinary.ch1, EV_TESTED);
7506 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007507 skipping:
7508 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007509 evalskip = 0;
7510 continue;
7511 }
7512 if (evalskip == SKIPBREAK && --skipcount <= 0)
7513 evalskip = 0;
7514 break;
7515 }
Eric Andersenc470f442003-07-28 09:56:35 +00007516 i = exitstatus;
7517 if (n->type != NWHILE)
7518 i = !i;
7519 if (i != 0)
7520 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007521 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007522 status = exitstatus;
7523 if (evalskip)
7524 goto skipping;
7525 }
7526 loopnest--;
7527 exitstatus = status;
7528}
7529
Eric Andersenc470f442003-07-28 09:56:35 +00007530static void
7531evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007532{
7533 struct arglist arglist;
7534 union node *argp;
7535 struct strlist *sp;
7536 struct stackmark smark;
7537
7538 setstackmark(&smark);
7539 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007540 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007541 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007542 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007543 if (evalskip)
7544 goto out;
7545 }
7546 *arglist.lastp = NULL;
7547
7548 exitstatus = 0;
7549 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007550 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007551 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007552 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007553 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007554 if (evalskip) {
7555 if (evalskip == SKIPCONT && --skipcount <= 0) {
7556 evalskip = 0;
7557 continue;
7558 }
7559 if (evalskip == SKIPBREAK && --skipcount <= 0)
7560 evalskip = 0;
7561 break;
7562 }
7563 }
7564 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007565 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007566 popstackmark(&smark);
7567}
7568
Eric Andersenc470f442003-07-28 09:56:35 +00007569static void
7570evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007571{
7572 union node *cp;
7573 union node *patp;
7574 struct arglist arglist;
7575 struct stackmark smark;
7576
7577 setstackmark(&smark);
7578 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007579 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007580 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007581 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7582 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007583 if (casematch(patp, arglist.list->text)) {
7584 if (evalskip == 0) {
7585 evaltree(cp->nclist.body, flags);
7586 }
7587 goto out;
7588 }
7589 }
7590 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007591 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007592 popstackmark(&smark);
7593}
7594
Eric Andersenc470f442003-07-28 09:56:35 +00007595/*
7596 * Kick off a subshell to evaluate a tree.
7597 */
Eric Andersenc470f442003-07-28 09:56:35 +00007598static void
7599evalsubshell(union node *n, int flags)
7600{
7601 struct job *jp;
7602 int backgnd = (n->type == NBACKGND);
7603 int status;
7604
7605 expredir(n->nredir.redirect);
7606 if (!backgnd && flags & EV_EXIT && !trap[0])
7607 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007608 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007609 jp = makejob(n, 1);
7610 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007611 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007612 flags |= EV_EXIT;
7613 if (backgnd)
7614 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007615 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007616 redirect(n->nredir.redirect, 0);
7617 evaltreenr(n->nredir.n, flags);
7618 /* never returns */
7619 }
7620 status = 0;
7621 if (! backgnd)
7622 status = waitforjob(jp);
7623 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007624 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007625}
7626
Eric Andersenc470f442003-07-28 09:56:35 +00007627/*
7628 * Compute the names of the files in a redirection list.
7629 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007630static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007631static void
7632expredir(union node *n)
7633{
7634 union node *redir;
7635
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007636 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007637 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007638
7639 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007640 fn.lastp = &fn.list;
7641 switch (redir->type) {
7642 case NFROMTO:
7643 case NFROM:
7644 case NTO:
7645 case NCLOBBER:
7646 case NAPPEND:
7647 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7648 redir->nfile.expfname = fn.list->text;
7649 break;
7650 case NFROMFD:
7651 case NTOFD:
7652 if (redir->ndup.vname) {
7653 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007654 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007655 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007656 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007657 }
7658 break;
7659 }
7660 }
7661}
7662
Eric Andersencb57d552001-06-28 07:25:16 +00007663/*
Eric Andersencb57d552001-06-28 07:25:16 +00007664 * Evaluate a pipeline. All the processes in the pipeline are children
7665 * of the process creating the pipeline. (This differs from some versions
7666 * of the shell, which make the last process in a pipeline the parent
7667 * of all the rest.)
7668 */
Eric Andersenc470f442003-07-28 09:56:35 +00007669static void
7670evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007671{
7672 struct job *jp;
7673 struct nodelist *lp;
7674 int pipelen;
7675 int prevfd;
7676 int pip[2];
7677
Eric Andersenc470f442003-07-28 09:56:35 +00007678 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007679 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007680 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007681 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007682 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007683 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007684 jp = makejob(n, pipelen);
7685 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007686 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007687 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007688 pip[1] = -1;
7689 if (lp->next) {
7690 if (pipe(pip) < 0) {
7691 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007692 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007693 }
7694 }
7695 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007696 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007697 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007698 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007699 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007700 if (prevfd > 0) {
7701 dup2(prevfd, 0);
7702 close(prevfd);
7703 }
7704 if (pip[1] > 1) {
7705 dup2(pip[1], 1);
7706 close(pip[1]);
7707 }
Eric Andersenc470f442003-07-28 09:56:35 +00007708 evaltreenr(lp->n, flags);
7709 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007710 }
7711 if (prevfd >= 0)
7712 close(prevfd);
7713 prevfd = pip[0];
7714 close(pip[1]);
7715 }
Eric Andersencb57d552001-06-28 07:25:16 +00007716 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007717 exitstatus = waitforjob(jp);
7718 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007719 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007720 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007721}
7722
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007723/*
7724 * Controls whether the shell is interactive or not.
7725 */
7726static void
7727setinteractive(int on)
7728{
7729 static int is_interactive;
7730
7731 if (++on == is_interactive)
7732 return;
7733 is_interactive = on;
7734 setsignal(SIGINT);
7735 setsignal(SIGQUIT);
7736 setsignal(SIGTERM);
7737#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7738 if (is_interactive > 1) {
7739 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007740 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007741
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007742 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007743 out1fmt(
7744 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007745 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007746 "Enter 'help' for a list of built-in commands."
7747 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007748 bb_banner);
7749 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007750 }
7751 }
7752#endif
7753}
7754
7755#if ENABLE_FEATURE_EDITING_VI
7756#define setvimode(on) do { \
7757 if (on) line_input_state->flags |= VI_MODE; \
7758 else line_input_state->flags &= ~VI_MODE; \
7759} while (0)
7760#else
7761#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7762#endif
7763
7764static void
7765optschanged(void)
7766{
7767#if DEBUG
7768 opentrace();
7769#endif
7770 setinteractive(iflag);
7771 setjobctl(mflag);
7772 setvimode(viflag);
7773}
7774
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007775static struct localvar *localvars;
7776
7777/*
7778 * Called after a function returns.
7779 * Interrupts must be off.
7780 */
7781static void
7782poplocalvars(void)
7783{
7784 struct localvar *lvp;
7785 struct var *vp;
7786
7787 while ((lvp = localvars) != NULL) {
7788 localvars = lvp->next;
7789 vp = lvp->vp;
7790 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7791 if (vp == NULL) { /* $- saved */
7792 memcpy(optlist, lvp->text, sizeof(optlist));
7793 free((char*)lvp->text);
7794 optschanged();
7795 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7796 unsetvar(vp->text);
7797 } else {
7798 if (vp->func)
7799 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7800 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7801 free((char*)vp->text);
7802 vp->flags = lvp->flags;
7803 vp->text = lvp->text;
7804 }
7805 free(lvp);
7806 }
7807}
7808
7809static int
7810evalfun(struct funcnode *func, int argc, char **argv, int flags)
7811{
7812 volatile struct shparam saveparam;
7813 struct localvar *volatile savelocalvars;
7814 struct jmploc *volatile savehandler;
7815 struct jmploc jmploc;
7816 int e;
7817
7818 saveparam = shellparam;
7819 savelocalvars = localvars;
7820 e = setjmp(jmploc.loc);
7821 if (e) {
7822 goto funcdone;
7823 }
7824 INT_OFF;
7825 savehandler = exception_handler;
7826 exception_handler = &jmploc;
7827 localvars = NULL;
7828 shellparam.malloc = 0;
7829 func->count++;
7830 funcnest++;
7831 INT_ON;
7832 shellparam.nparam = argc - 1;
7833 shellparam.p = argv + 1;
7834#if ENABLE_ASH_GETOPTS
7835 shellparam.optind = 1;
7836 shellparam.optoff = -1;
7837#endif
7838 evaltree(&func->n, flags & EV_TESTED);
7839funcdone:
7840 INT_OFF;
7841 funcnest--;
7842 freefunc(func);
7843 poplocalvars();
7844 localvars = savelocalvars;
7845 freeparam(&shellparam);
7846 shellparam = saveparam;
7847 exception_handler = savehandler;
7848 INT_ON;
7849 evalskip &= ~SKIPFUNC;
7850 return e;
7851}
7852
Denis Vlasenko131ae172007-02-18 13:00:19 +00007853#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007854static char **
7855parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007856{
7857 char *cp, c;
7858
7859 for (;;) {
7860 cp = *++argv;
7861 if (!cp)
7862 return 0;
7863 if (*cp++ != '-')
7864 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007865 c = *cp++;
7866 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007867 break;
7868 if (c == '-' && !*cp) {
7869 argv++;
7870 break;
7871 }
7872 do {
7873 switch (c) {
7874 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00007875 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00007876 break;
7877 default:
7878 /* run 'typecmd' for other options */
7879 return 0;
7880 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007881 c = *cp++;
7882 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007883 }
7884 return argv;
7885}
7886#endif
7887
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007888/*
7889 * Make a variable a local variable. When a variable is made local, it's
7890 * value and flags are saved in a localvar structure. The saved values
7891 * will be restored when the shell function returns. We handle the name
7892 * "-" as a special case.
7893 */
7894static void
7895mklocal(char *name)
7896{
7897 struct localvar *lvp;
7898 struct var **vpp;
7899 struct var *vp;
7900
7901 INT_OFF;
7902 lvp = ckmalloc(sizeof(struct localvar));
7903 if (LONE_DASH(name)) {
7904 char *p;
7905 p = ckmalloc(sizeof(optlist));
7906 lvp->text = memcpy(p, optlist, sizeof(optlist));
7907 vp = NULL;
7908 } else {
7909 char *eq;
7910
7911 vpp = hashvar(name);
7912 vp = *findvar(vpp, name);
7913 eq = strchr(name, '=');
7914 if (vp == NULL) {
7915 if (eq)
7916 setvareq(name, VSTRFIXED);
7917 else
7918 setvar(name, NULL, VSTRFIXED);
7919 vp = *vpp; /* the new variable */
7920 lvp->flags = VUNSET;
7921 } else {
7922 lvp->text = vp->text;
7923 lvp->flags = vp->flags;
7924 vp->flags |= VSTRFIXED|VTEXTFIXED;
7925 if (eq)
7926 setvareq(name, 0);
7927 }
7928 }
7929 lvp->vp = vp;
7930 lvp->next = localvars;
7931 localvars = lvp;
7932 INT_ON;
7933}
7934
7935/*
7936 * The "local" command.
7937 */
7938static int
7939localcmd(int argc, char **argv)
7940{
7941 char *name;
7942
7943 argv = argptr;
7944 while ((name = *argv++) != NULL) {
7945 mklocal(name);
7946 }
7947 return 0;
7948}
7949
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00007950static int
7951falsecmd(int argc, char **argv)
7952{
7953 return 1;
7954}
7955
7956static int
7957truecmd(int argc, char **argv)
7958{
7959 return 0;
7960}
7961
7962static int
7963execcmd(int argc, char **argv)
7964{
7965 if (argc > 1) {
7966 iflag = 0; /* exit on error */
7967 mflag = 0;
7968 optschanged();
7969 shellexec(argv + 1, pathval(), 0);
7970 }
7971 return 0;
7972}
7973
7974/*
7975 * The return command.
7976 */
7977static int
7978returncmd(int argc, char **argv)
7979{
7980 /*
7981 * If called outside a function, do what ksh does;
7982 * skip the rest of the file.
7983 */
7984 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
7985 return argv[1] ? number(argv[1]) : exitstatus;
7986}
7987
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007988/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007989static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007990static int dotcmd(int, char **);
7991static int evalcmd(int, char **);
7992#if ENABLE_ASH_BUILTIN_ECHO
7993static int echocmd(int, char **);
7994#endif
7995#if ENABLE_ASH_BUILTIN_TEST
7996static int testcmd(int, char **);
7997#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007998static int exitcmd(int, char **);
7999static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008000#if ENABLE_ASH_GETOPTS
8001static int getoptscmd(int, char **);
8002#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008003#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8004static int helpcmd(int argc, char **argv);
8005#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008006#if ENABLE_ASH_MATH_SUPPORT
8007static int letcmd(int, char **);
8008#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008009static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008010static int setcmd(int, char **);
8011static int shiftcmd(int, char **);
8012static int timescmd(int, char **);
8013static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008014static int umaskcmd(int, char **);
8015static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008016static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008017
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008018#define BUILTIN_NOSPEC "0"
8019#define BUILTIN_SPECIAL "1"
8020#define BUILTIN_REGULAR "2"
8021#define BUILTIN_SPEC_REG "3"
8022#define BUILTIN_ASSIGN "4"
8023#define BUILTIN_SPEC_ASSG "5"
8024#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008025#define BUILTIN_SPEC_REG_ASSG "7"
8026
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008027/* make sure to keep these in proper order since it is searched via bsearch() */
8028static const struct builtincmd builtintab[] = {
8029 { BUILTIN_SPEC_REG ".", dotcmd },
8030 { BUILTIN_SPEC_REG ":", truecmd },
8031#if ENABLE_ASH_BUILTIN_TEST
8032 { BUILTIN_REGULAR "[", testcmd },
8033 { BUILTIN_REGULAR "[[", testcmd },
8034#endif
8035#if ENABLE_ASH_ALIAS
8036 { BUILTIN_REG_ASSG "alias", aliascmd },
8037#endif
8038#if JOBS
8039 { BUILTIN_REGULAR "bg", fg_bgcmd },
8040#endif
8041 { BUILTIN_SPEC_REG "break", breakcmd },
8042 { BUILTIN_REGULAR "cd", cdcmd },
8043 { BUILTIN_NOSPEC "chdir", cdcmd },
8044#if ENABLE_ASH_CMDCMD
8045 { BUILTIN_REGULAR "command", commandcmd },
8046#endif
8047 { BUILTIN_SPEC_REG "continue", breakcmd },
8048#if ENABLE_ASH_BUILTIN_ECHO
8049 { BUILTIN_REGULAR "echo", echocmd },
8050#endif
8051 { BUILTIN_SPEC_REG "eval", evalcmd },
8052 { BUILTIN_SPEC_REG "exec", execcmd },
8053 { BUILTIN_SPEC_REG "exit", exitcmd },
8054 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8055 { BUILTIN_REGULAR "false", falsecmd },
8056#if JOBS
8057 { BUILTIN_REGULAR "fg", fg_bgcmd },
8058#endif
8059#if ENABLE_ASH_GETOPTS
8060 { BUILTIN_REGULAR "getopts", getoptscmd },
8061#endif
8062 { BUILTIN_NOSPEC "hash", hashcmd },
8063#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8064 { BUILTIN_NOSPEC "help", helpcmd },
8065#endif
8066#if JOBS
8067 { BUILTIN_REGULAR "jobs", jobscmd },
8068 { BUILTIN_REGULAR "kill", killcmd },
8069#endif
8070#if ENABLE_ASH_MATH_SUPPORT
8071 { BUILTIN_NOSPEC "let", letcmd },
8072#endif
8073 { BUILTIN_ASSIGN "local", localcmd },
8074 { BUILTIN_NOSPEC "pwd", pwdcmd },
8075 { BUILTIN_REGULAR "read", readcmd },
8076 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8077 { BUILTIN_SPEC_REG "return", returncmd },
8078 { BUILTIN_SPEC_REG "set", setcmd },
8079 { BUILTIN_SPEC_REG "shift", shiftcmd },
8080 { BUILTIN_SPEC_REG "source", dotcmd },
8081#if ENABLE_ASH_BUILTIN_TEST
8082 { BUILTIN_REGULAR "test", testcmd },
8083#endif
8084 { BUILTIN_SPEC_REG "times", timescmd },
8085 { BUILTIN_SPEC_REG "trap", trapcmd },
8086 { BUILTIN_REGULAR "true", truecmd },
8087 { BUILTIN_NOSPEC "type", typecmd },
8088 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8089 { BUILTIN_REGULAR "umask", umaskcmd },
8090#if ENABLE_ASH_ALIAS
8091 { BUILTIN_REGULAR "unalias", unaliascmd },
8092#endif
8093 { BUILTIN_SPEC_REG "unset", unsetcmd },
8094 { BUILTIN_REGULAR "wait", waitcmd },
8095};
8096
8097#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
8098
8099#define COMMANDCMD (builtintab + 5 + \
8100 2 * ENABLE_ASH_BUILTIN_TEST + \
8101 ENABLE_ASH_ALIAS + \
8102 ENABLE_ASH_JOB_CONTROL)
8103#define EXECCMD (builtintab + 7 + \
8104 2 * ENABLE_ASH_BUILTIN_TEST + \
8105 ENABLE_ASH_ALIAS + \
8106 ENABLE_ASH_JOB_CONTROL + \
8107 ENABLE_ASH_CMDCMD + \
8108 ENABLE_ASH_BUILTIN_ECHO)
8109
8110/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008111 * Search the table of builtin commands.
8112 */
8113static struct builtincmd *
8114find_builtin(const char *name)
8115{
8116 struct builtincmd *bp;
8117
8118 bp = bsearch(
8119 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
8120 pstrcmp
8121 );
8122 return bp;
8123}
8124
8125/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008126 * Execute a simple command.
8127 */
8128static int back_exitstatus; /* exit status of backquoted command */
8129static int
8130isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008131{
8132 const char *q = endofname(p);
8133 if (p == q)
8134 return 0;
8135 return *q == '=';
8136}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008137static int
8138bltincmd(int argc, char **argv)
8139{
8140 /* Preserve exitstatus of a previous possible redirection
8141 * as POSIX mandates */
8142 return back_exitstatus;
8143}
Eric Andersenc470f442003-07-28 09:56:35 +00008144static void
8145evalcommand(union node *cmd, int flags)
8146{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008147 static const struct builtincmd bltin = {
8148 "\0\0", bltincmd
8149 };
Eric Andersenc470f442003-07-28 09:56:35 +00008150 struct stackmark smark;
8151 union node *argp;
8152 struct arglist arglist;
8153 struct arglist varlist;
8154 char **argv;
8155 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008156 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008157 struct cmdentry cmdentry;
8158 struct job *jp;
8159 char *lastarg;
8160 const char *path;
8161 int spclbltin;
8162 int cmd_is_exec;
8163 int status;
8164 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008165 struct builtincmd *bcmd;
8166 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008167
8168 /* First expand the arguments. */
8169 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8170 setstackmark(&smark);
8171 back_exitstatus = 0;
8172
8173 cmdentry.cmdtype = CMDBUILTIN;
8174 cmdentry.u.cmd = &bltin;
8175 varlist.lastp = &varlist.list;
8176 *varlist.lastp = NULL;
8177 arglist.lastp = &arglist.list;
8178 *arglist.lastp = NULL;
8179
8180 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008181 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008182 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8183 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8184 }
8185
Eric Andersenc470f442003-07-28 09:56:35 +00008186 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8187 struct strlist **spp;
8188
8189 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008190 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008191 expandarg(argp, &arglist, EXP_VARTILDE);
8192 else
8193 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8194
Eric Andersenc470f442003-07-28 09:56:35 +00008195 for (sp = *spp; sp; sp = sp->next)
8196 argc++;
8197 }
8198
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008199 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008200 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008201 TRACE(("evalcommand arg: %s\n", sp->text));
8202 *nargv++ = sp->text;
8203 }
8204 *nargv = NULL;
8205
8206 lastarg = NULL;
8207 if (iflag && funcnest == 0 && argc > 0)
8208 lastarg = nargv[-1];
8209
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008210 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008211 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008212 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008213
8214 path = vpath.text;
8215 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8216 struct strlist **spp;
8217 char *p;
8218
8219 spp = varlist.lastp;
8220 expandarg(argp, &varlist, EXP_VARTILDE);
8221
8222 /*
8223 * Modify the command lookup path, if a PATH= assignment
8224 * is present
8225 */
8226 p = (*spp)->text;
8227 if (varequal(p, path))
8228 path = p;
8229 }
8230
8231 /* Print the command if xflag is set. */
8232 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008233 int n;
8234 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008235
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008236 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008237 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008238
8239 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008240 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008241 while (sp) {
8242 dprintf(preverrout_fd, p, sp->text);
8243 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008244 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008245 p--;
8246 }
8247 }
8248 sp = arglist.list;
8249 }
Rob Landley53437472006-07-16 08:14:35 +00008250 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008251 }
8252
8253 cmd_is_exec = 0;
8254 spclbltin = -1;
8255
8256 /* Now locate the command. */
8257 if (argc) {
8258 const char *oldpath;
8259 int cmd_flag = DO_ERR;
8260
8261 path += 5;
8262 oldpath = path;
8263 for (;;) {
8264 find_command(argv[0], &cmdentry, cmd_flag, path);
8265 if (cmdentry.cmdtype == CMDUNKNOWN) {
8266 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008267 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008268 goto bail;
8269 }
8270
8271 /* implement bltin and command here */
8272 if (cmdentry.cmdtype != CMDBUILTIN)
8273 break;
8274 if (spclbltin < 0)
8275 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8276 if (cmdentry.u.cmd == EXECCMD)
8277 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008278#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008279 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008280 path = oldpath;
8281 nargv = parse_command_args(argv, &path);
8282 if (!nargv)
8283 break;
8284 argc -= nargv - argv;
8285 argv = nargv;
8286 cmd_flag |= DO_NOFUNC;
8287 } else
8288#endif
8289 break;
8290 }
8291 }
8292
8293 if (status) {
8294 /* We have a redirection error. */
8295 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008296 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008297 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008298 exitstatus = status;
8299 goto out;
8300 }
8301
8302 /* Execute the command. */
8303 switch (cmdentry.cmdtype) {
8304 default:
8305 /* Fork off a child process if necessary. */
8306 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008307 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008308 jp = makejob(cmd, 1);
8309 if (forkshell(jp, cmd, FORK_FG) != 0) {
8310 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008311 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008312 break;
8313 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008314 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008315 }
8316 listsetvar(varlist.list, VEXPORT|VSTACK);
8317 shellexec(argv, path, cmdentry.u.index);
8318 /* NOTREACHED */
8319
8320 case CMDBUILTIN:
8321 cmdenviron = varlist.list;
8322 if (cmdenviron) {
8323 struct strlist *list = cmdenviron;
8324 int i = VNOSET;
8325 if (spclbltin > 0 || argc == 0) {
8326 i = 0;
8327 if (cmd_is_exec && argc > 1)
8328 i = VEXPORT;
8329 }
8330 listsetvar(list, i);
8331 }
8332 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8333 int exit_status;
8334 int i, j;
8335
8336 i = exception;
8337 if (i == EXEXIT)
8338 goto raise;
8339
8340 exit_status = 2;
8341 j = 0;
8342 if (i == EXINT)
8343 j = SIGINT;
8344 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008345 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008346 if (j)
8347 exit_status = j + 128;
8348 exitstatus = exit_status;
8349
8350 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008351 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008352 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008353 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008354 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008355 }
8356 break;
8357
8358 case CMDFUNCTION:
8359 listsetvar(varlist.list, 0);
8360 if (evalfun(cmdentry.u.func, argc, argv, flags))
8361 goto raise;
8362 break;
8363 }
8364
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008365 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008366 popredir(cmd_is_exec);
8367 if (lastarg)
8368 /* dsl: I think this is intended to be used to support
8369 * '_' in 'vi' command mode during line editing...
8370 * However I implemented that within libedit itself.
8371 */
8372 setvar("_", lastarg, 0);
8373 popstackmark(&smark);
8374}
8375
8376static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008377evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8378{
Eric Andersenc470f442003-07-28 09:56:35 +00008379 char *volatile savecmdname;
8380 struct jmploc *volatile savehandler;
8381 struct jmploc jmploc;
8382 int i;
8383
8384 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008385 i = setjmp(jmploc.loc);
8386 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008387 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008388 savehandler = exception_handler;
8389 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008390 commandname = argv[0];
8391 argptr = argv + 1;
8392 optptr = NULL; /* initialize nextopt */
8393 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008394 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008395 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008396 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008397 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008398 commandname = savecmdname;
8399 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008400 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008401
8402 return i;
8403}
8404
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008405static int
8406goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008407{
8408 return !*endofname(p);
8409}
8410
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008411
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008412/*
8413 * Search for a command. This is called before we fork so that the
8414 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008415 * the child. The check for "goodname" is an overly conservative
8416 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008417 */
Eric Andersenc470f442003-07-28 09:56:35 +00008418static void
8419prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008420{
8421 struct cmdentry entry;
8422
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008423 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8424 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008425}
8426
Eric Andersencb57d552001-06-28 07:25:16 +00008427
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008428/* ============ Builtin commands
8429 *
8430 * Builtin commands whose functions are closely tied to evaluation
8431 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008432 */
8433
8434/*
Eric Andersencb57d552001-06-28 07:25:16 +00008435 * Handle break and continue commands. Break, continue, and return are
8436 * all handled by setting the evalskip flag. The evaluation routines
8437 * above all check this flag, and if it is set they start skipping
8438 * commands rather than executing them. The variable skipcount is
8439 * the number of loops to break/continue, or the number of function
8440 * levels to return. (The latter is always 1.) It should probably
8441 * be an error to break out of more loops than exist, but it isn't
8442 * in the standard shell so we don't make it one here.
8443 */
Eric Andersenc470f442003-07-28 09:56:35 +00008444static int
8445breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008446{
8447 int n = argc > 1 ? number(argv[1]) : 1;
8448
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008449 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008450 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008451 if (n > loopnest)
8452 n = loopnest;
8453 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008454 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008455 skipcount = n;
8456 }
8457 return 0;
8458}
8459
Eric Andersenc470f442003-07-28 09:56:35 +00008460
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008461/* ============ input.c
8462 *
Eric Andersen90898442003-08-06 11:20:52 +00008463 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008464 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008465
Eric Andersenc470f442003-07-28 09:56:35 +00008466#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008467
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008468enum {
8469 INPUT_PUSH_FILE = 1,
8470 INPUT_NOFILE_OK = 2,
8471};
Eric Andersencb57d552001-06-28 07:25:16 +00008472
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008473/*
8474 * NEOF is returned by parsecmd when it encounters an end of file. It
8475 * must be distinct from NULL, so we use the address of a variable that
8476 * happens to be handy.
8477 */
8478static int plinno = 1; /* input line number */
8479/* number of characters left in input buffer */
8480static int parsenleft; /* copy of parsefile->nleft */
8481static int parselleft; /* copy of parsefile->lleft */
8482/* next character in input buffer */
8483static char *parsenextc; /* copy of parsefile->nextc */
8484
8485static int checkkwd;
8486/* values of checkkwd variable */
8487#define CHKALIAS 0x1
8488#define CHKKWD 0x2
8489#define CHKNL 0x4
8490
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008491static void
8492popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008493{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008494 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008495
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008496 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008497#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008498 if (sp->ap) {
8499 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8500 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008501 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008502 if (sp->string != sp->ap->val) {
8503 free(sp->string);
8504 }
8505 sp->ap->flag &= ~ALIASINUSE;
8506 if (sp->ap->flag & ALIASDEAD) {
8507 unalias(sp->ap->name);
8508 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008509 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008510#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008511 parsenextc = sp->prevstring;
8512 parsenleft = sp->prevnleft;
8513/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8514 parsefile->strpush = sp->prev;
8515 if (sp != &(parsefile->basestrpush))
8516 free(sp);
8517 INT_ON;
8518}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008519
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008520static int
8521preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008522{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008523 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008524 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008525 parsenextc = buf;
8526
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008527 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008528#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008529 if (!iflag || parsefile->fd)
8530 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8531 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008532#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008533 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008534#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008535 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8536 if (nr == 0) {
8537 /* Ctrl+C pressed */
8538 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008539 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008540 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008541 raise(SIGINT);
8542 return 1;
8543 }
Eric Andersenc470f442003-07-28 09:56:35 +00008544 goto retry;
8545 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008546 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008547 /* Ctrl+D presend */
8548 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008549 }
Eric Andersencb57d552001-06-28 07:25:16 +00008550 }
8551#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008552 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008553#endif
8554
8555 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008556 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
8557 int flags = fcntl(0, F_GETFL, 0);
8558 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008559 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008560 if (fcntl(0, F_SETFL, flags) >= 0) {
8561 out2str("sh: turning off NDELAY mode\n");
8562 goto retry;
8563 }
8564 }
8565 }
8566 }
8567 return nr;
8568}
8569
8570/*
8571 * Refill the input buffer and return the next input character:
8572 *
8573 * 1) If a string was pushed back on the input, pop it;
8574 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8575 * from a string so we can't refill the buffer, return EOF.
8576 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8577 * 4) Process input up to the next newline, deleting nul characters.
8578 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008579static int
Eric Andersenc470f442003-07-28 09:56:35 +00008580preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008581{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008582 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008583 int more;
8584 char savec;
8585
8586 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008587#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008588 if (parsenleft == -1 && parsefile->strpush->ap &&
8589 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008590 return PEOA;
8591 }
Eric Andersen2870d962001-07-02 17:27:21 +00008592#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008593 popstring();
8594 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008595 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008596 }
8597 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8598 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008599 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008600
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008601 more = parselleft;
8602 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008603 again:
8604 more = preadfd();
8605 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008606 parselleft = parsenleft = EOF_NLEFT;
8607 return PEOF;
8608 }
8609 }
8610
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008611 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008612
8613 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008614 for (;;) {
8615 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008616
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008617 more--;
8618 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008619
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008620 if (!c)
8621 memmove(q, q + 1, more);
8622 else {
8623 q++;
8624 if (c == '\n') {
8625 parsenleft = q - parsenextc - 1;
8626 break;
8627 }
Eric Andersencb57d552001-06-28 07:25:16 +00008628 }
8629
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008630 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008631 parsenleft = q - parsenextc - 1;
8632 if (parsenleft < 0)
8633 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008634 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008635 }
8636 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008637 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008638
8639 savec = *q;
8640 *q = '\0';
8641
8642 if (vflag) {
8643 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008644 }
8645
8646 *q = savec;
8647
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008648 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008649}
8650
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008651#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008652static int
8653pgetc(void)
8654{
8655 return pgetc_as_macro();
8656}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008657
8658#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8659#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008660#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008661#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008662#endif
8663
8664/*
8665 * Same as pgetc(), but ignores PEOA.
8666 */
8667#if ENABLE_ASH_ALIAS
8668static int
8669pgetc2(void)
8670{
8671 int c;
8672
8673 do {
8674 c = pgetc_macro();
8675 } while (c == PEOA);
8676 return c;
8677}
8678#else
8679static int
8680pgetc2(void)
8681{
8682 return pgetc_macro();
8683}
8684#endif
8685
8686/*
8687 * Read a line from the script.
8688 */
8689static char *
8690pfgets(char *line, int len)
8691{
8692 char *p = line;
8693 int nleft = len;
8694 int c;
8695
8696 while (--nleft > 0) {
8697 c = pgetc2();
8698 if (c == PEOF) {
8699 if (p == line)
8700 return NULL;
8701 break;
8702 }
8703 *p++ = c;
8704 if (c == '\n')
8705 break;
8706 }
8707 *p = '\0';
8708 return line;
8709}
8710
Eric Andersenc470f442003-07-28 09:56:35 +00008711/*
8712 * Undo the last call to pgetc. Only one character may be pushed back.
8713 * PEOF may be pushed back.
8714 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008715static void
Eric Andersenc470f442003-07-28 09:56:35 +00008716pungetc(void)
8717{
8718 parsenleft++;
8719 parsenextc--;
8720}
Eric Andersencb57d552001-06-28 07:25:16 +00008721
8722/*
8723 * Push a string back onto the input at this current parsefile level.
8724 * We handle aliases this way.
8725 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008726static void
Eric Andersenc470f442003-07-28 09:56:35 +00008727pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008728{
Eric Andersencb57d552001-06-28 07:25:16 +00008729 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008730 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008731
Eric Andersenc470f442003-07-28 09:56:35 +00008732 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008733 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008734/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8735 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008736 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008737 sp->prev = parsefile->strpush;
8738 parsefile->strpush = sp;
8739 } else
8740 sp = parsefile->strpush = &(parsefile->basestrpush);
8741 sp->prevstring = parsenextc;
8742 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008743#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008744 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008745 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008746 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008747 sp->string = s;
8748 }
Eric Andersen2870d962001-07-02 17:27:21 +00008749#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008750 parsenextc = s;
8751 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008752 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008753}
8754
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008755/*
8756 * To handle the "." command, a stack of input files is used. Pushfile
8757 * adds a new entry to the stack and popfile restores the previous level.
8758 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008759static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008760pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008761{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008762 struct parsefile *pf;
8763
8764 parsefile->nleft = parsenleft;
8765 parsefile->lleft = parselleft;
8766 parsefile->nextc = parsenextc;
8767 parsefile->linno = plinno;
8768 pf = ckmalloc(sizeof(*pf));
8769 pf->prev = parsefile;
8770 pf->fd = -1;
8771 pf->strpush = NULL;
8772 pf->basestrpush.prev = NULL;
8773 parsefile = pf;
8774}
8775
8776static void
8777popfile(void)
8778{
8779 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008780
Denis Vlasenkob012b102007-02-19 22:43:01 +00008781 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008782 if (pf->fd >= 0)
8783 close(pf->fd);
8784 if (pf->buf)
8785 free(pf->buf);
8786 while (pf->strpush)
8787 popstring();
8788 parsefile = pf->prev;
8789 free(pf);
8790 parsenleft = parsefile->nleft;
8791 parselleft = parsefile->lleft;
8792 parsenextc = parsefile->nextc;
8793 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008794 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008795}
8796
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008797/*
8798 * Return to top level.
8799 */
8800static void
8801popallfiles(void)
8802{
8803 while (parsefile != &basepf)
8804 popfile();
8805}
8806
8807/*
8808 * Close the file(s) that the shell is reading commands from. Called
8809 * after a fork is done.
8810 */
8811static void
8812closescript(void)
8813{
8814 popallfiles();
8815 if (parsefile->fd > 0) {
8816 close(parsefile->fd);
8817 parsefile->fd = 0;
8818 }
8819}
8820
8821/*
8822 * Like setinputfile, but takes an open file descriptor. Call this with
8823 * interrupts off.
8824 */
8825static void
8826setinputfd(int fd, int push)
8827{
8828 fcntl(fd, F_SETFD, FD_CLOEXEC);
8829 if (push) {
8830 pushfile();
8831 parsefile->buf = 0;
8832 }
8833 parsefile->fd = fd;
8834 if (parsefile->buf == NULL)
8835 parsefile->buf = ckmalloc(IBUFSIZ);
8836 parselleft = parsenleft = 0;
8837 plinno = 1;
8838}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008839
Eric Andersenc470f442003-07-28 09:56:35 +00008840/*
8841 * Set the input to take input from a file. If push is set, push the
8842 * old input onto the stack first.
8843 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008844static int
8845setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008846{
8847 int fd;
8848 int fd2;
8849
Denis Vlasenkob012b102007-02-19 22:43:01 +00008850 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008851 fd = open(fname, O_RDONLY);
8852 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008853 if (flags & INPUT_NOFILE_OK)
8854 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008855 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008856 }
Eric Andersenc470f442003-07-28 09:56:35 +00008857 if (fd < 10) {
8858 fd2 = copyfd(fd, 10);
8859 close(fd);
8860 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008861 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008862 fd = fd2;
8863 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008864 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008865 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008866 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008867 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008868}
8869
Eric Andersencb57d552001-06-28 07:25:16 +00008870/*
8871 * Like setinputfile, but takes input from a string.
8872 */
Eric Andersenc470f442003-07-28 09:56:35 +00008873static void
8874setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008875{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008876 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008877 pushfile();
8878 parsenextc = string;
8879 parsenleft = strlen(string);
8880 parsefile->buf = NULL;
8881 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008882 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008883}
8884
8885
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008886/* ============ mail.c
8887 *
8888 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008889 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008890
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008891#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008892
Eric Andersencb57d552001-06-28 07:25:16 +00008893#define MAXMBOXES 10
8894
Eric Andersenc470f442003-07-28 09:56:35 +00008895/* times of mailboxes */
8896static time_t mailtime[MAXMBOXES];
8897/* Set if MAIL or MAILPATH is changed. */
8898static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008899
Eric Andersencb57d552001-06-28 07:25:16 +00008900/*
Eric Andersenc470f442003-07-28 09:56:35 +00008901 * Print appropriate message(s) if mail has arrived.
8902 * If mail_var_path_changed is set,
8903 * then the value of MAIL has mail_var_path_changed,
8904 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008905 */
Eric Andersenc470f442003-07-28 09:56:35 +00008906static void
8907chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008908{
Eric Andersencb57d552001-06-28 07:25:16 +00008909 const char *mpath;
8910 char *p;
8911 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008912 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008913 struct stackmark smark;
8914 struct stat statb;
8915
Eric Andersencb57d552001-06-28 07:25:16 +00008916 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008917 mpath = mpathset() ? mpathval() : mailval();
8918 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008919 p = padvance(&mpath, nullstr);
8920 if (p == NULL)
8921 break;
8922 if (*p == '\0')
8923 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008924 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008925#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008926 if (q[-1] != '/')
8927 abort();
8928#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008929 q[-1] = '\0'; /* delete trailing '/' */
8930 if (stat(p, &statb) < 0) {
8931 *mtp = 0;
8932 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008933 }
Eric Andersenc470f442003-07-28 09:56:35 +00008934 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8935 fprintf(
8936 stderr, snlfmt,
8937 pathopt ? pathopt : "you have mail"
8938 );
8939 }
8940 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008941 }
Eric Andersenc470f442003-07-28 09:56:35 +00008942 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008943 popstackmark(&smark);
8944}
Eric Andersencb57d552001-06-28 07:25:16 +00008945
Eric Andersenc470f442003-07-28 09:56:35 +00008946static void
8947changemail(const char *val)
8948{
8949 mail_var_path_changed++;
8950}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008951
Denis Vlasenko131ae172007-02-18 13:00:19 +00008952#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008953
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008954
8955/* ============ ??? */
8956
Eric Andersencb57d552001-06-28 07:25:16 +00008957/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008958 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00008959 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008960static void
8961setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008962{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008963 char **newparam;
8964 char **ap;
8965 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00008966
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008967 for (nparam = 0; argv[nparam]; nparam++);
8968 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
8969 while (*argv) {
8970 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008971 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008972 *ap = NULL;
8973 freeparam(&shellparam);
8974 shellparam.malloc = 1;
8975 shellparam.nparam = nparam;
8976 shellparam.p = newparam;
8977#if ENABLE_ASH_GETOPTS
8978 shellparam.optind = 1;
8979 shellparam.optoff = -1;
8980#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008981}
8982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008983/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008984 * Process shell options. The global variable argptr contains a pointer
8985 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008986 */
8987static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008988minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008989{
8990 int i;
8991
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008992 if (name) {
8993 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008994 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008995 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00008996 return;
8997 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008998 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008999 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009000 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009001 out1str("Current option settings\n");
9002 for (i = 0; i < NOPTS; i++)
9003 out1fmt("%-16s%s\n", optnames(i),
9004 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009005}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009006static void
9007setoption(int flag, int val)
9008{
9009 int i;
9010
9011 for (i = 0; i < NOPTS; i++) {
9012 if (optletters(i) == flag) {
9013 optlist[i] = val;
9014 return;
9015 }
9016 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009017 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009018 /* NOTREACHED */
9019}
Eric Andersenc470f442003-07-28 09:56:35 +00009020static void
9021options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009022{
9023 char *p;
9024 int val;
9025 int c;
9026
9027 if (cmdline)
9028 minusc = NULL;
9029 while ((p = *argptr) != NULL) {
9030 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009031 c = *p++;
9032 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009033 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009034 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009035 if (!cmdline) {
9036 /* "-" means turn off -x and -v */
9037 if (p[0] == '\0')
9038 xflag = vflag = 0;
9039 /* "--" means reset params */
9040 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009041 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009042 }
Eric Andersenc470f442003-07-28 09:56:35 +00009043 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009044 }
9045 } else if (c == '+') {
9046 val = 0;
9047 } else {
9048 argptr--;
9049 break;
9050 }
9051 while ((c = *p++) != '\0') {
9052 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00009053 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00009054 } else if (c == 'o') {
9055 minus_o(*argptr, val);
9056 if (*argptr)
9057 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00009058 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009059 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009060 isloginsh = 1;
9061 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009062 } else {
9063 setoption(c, val);
9064 }
9065 }
9066 }
9067}
9068
Eric Andersencb57d552001-06-28 07:25:16 +00009069/*
Eric Andersencb57d552001-06-28 07:25:16 +00009070 * The shift builtin command.
9071 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009072static int
Eric Andersenc470f442003-07-28 09:56:35 +00009073shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009074{
9075 int n;
9076 char **ap1, **ap2;
9077
9078 n = 1;
9079 if (argc > 1)
9080 n = number(argv[1]);
9081 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009082 ash_msg_and_raise_error("can't shift that many");
9083 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009084 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009085 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009086 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009087 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009088 }
9089 ap2 = shellparam.p;
9090 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009091#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009092 shellparam.optind = 1;
9093 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009094#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009095 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009096 return 0;
9097}
9098
Eric Andersencb57d552001-06-28 07:25:16 +00009099/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009100 * POSIX requires that 'set' (but not export or readonly) output the
9101 * variables in lexicographic order - by the locale's collating order (sigh).
9102 * Maybe we could keep them in an ordered balanced binary tree
9103 * instead of hashed lists.
9104 * For now just roll 'em through qsort for printing...
9105 */
9106static int
9107showvars(const char *sep_prefix, int on, int off)
9108{
9109 const char *sep;
9110 char **ep, **epend;
9111
9112 ep = listvars(on, off, &epend);
9113 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9114
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009115 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009116
9117 for (; ep < epend; ep++) {
9118 const char *p;
9119 const char *q;
9120
9121 p = strchrnul(*ep, '=');
9122 q = nullstr;
9123 if (*p)
9124 q = single_quote(++p);
9125 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9126 }
9127 return 0;
9128}
9129
9130/*
Eric Andersencb57d552001-06-28 07:25:16 +00009131 * The set command builtin.
9132 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009133static int
Eric Andersenc470f442003-07-28 09:56:35 +00009134setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009135{
9136 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009137 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009138 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009139 options(0);
9140 optschanged();
9141 if (*argptr != NULL) {
9142 setparam(argptr);
9143 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009144 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009145 return 0;
9146}
9147
Denis Vlasenko131ae172007-02-18 13:00:19 +00009148#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009149/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009150static void
9151change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009152{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009153 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009154 /* "get", generate */
9155 char buf[16];
9156
9157 rseed = rseed * 1103515245 + 12345;
9158 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9159 /* set without recursion */
9160 setvar(vrandom.text, buf, VNOFUNC);
9161 vrandom.flags &= ~VNOFUNC;
9162 } else {
9163 /* set/reset */
9164 rseed = strtoul(value, (char **)NULL, 10);
9165 }
Eric Andersenef02f822004-03-11 13:34:24 +00009166}
Eric Andersen16767e22004-03-16 05:14:10 +00009167#endif
9168
Denis Vlasenko131ae172007-02-18 13:00:19 +00009169#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009170static int
Eric Andersenc470f442003-07-28 09:56:35 +00009171getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009172{
9173 char *p, *q;
9174 char c = '?';
9175 int done = 0;
9176 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009177 char s[12];
9178 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009179
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009180 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009181 return 1;
9182 optnext = optfirst + *param_optind - 1;
9183
9184 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009185 p = NULL;
9186 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009187 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009188 if (p == NULL || *p == '\0') {
9189 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009190 p = *optnext;
9191 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009192 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009193 p = NULL;
9194 done = 1;
9195 goto out;
9196 }
9197 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009198 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009199 goto atend;
9200 }
9201
9202 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009203 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009204 if (*q == '\0') {
9205 if (optstr[0] == ':') {
9206 s[0] = c;
9207 s[1] = '\0';
9208 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009209 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009210 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009211 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009212 }
9213 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009214 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009215 }
9216 if (*++q == ':')
9217 q++;
9218 }
9219
9220 if (*++q == ':') {
9221 if (*p == '\0' && (p = *optnext) == NULL) {
9222 if (optstr[0] == ':') {
9223 s[0] = c;
9224 s[1] = '\0';
9225 err |= setvarsafe("OPTARG", s, 0);
9226 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009227 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009228 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009229 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009230 c = '?';
9231 }
Eric Andersenc470f442003-07-28 09:56:35 +00009232 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009233 }
9234
9235 if (p == *optnext)
9236 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009237 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009238 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009239 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009240 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009241 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009242 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009243 *param_optind = optnext - optfirst + 1;
9244 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009245 err |= setvarsafe("OPTIND", s, VNOFUNC);
9246 s[0] = c;
9247 s[1] = '\0';
9248 err |= setvarsafe(optvar, s, 0);
9249 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009250 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009251 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009252 flush_stdout_stderr();
9253 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009254 }
9255 return done;
9256}
Eric Andersenc470f442003-07-28 09:56:35 +00009257
9258/*
9259 * The getopts builtin. Shellparam.optnext points to the next argument
9260 * to be processed. Shellparam.optptr points to the next character to
9261 * be processed in the current argument. If shellparam.optnext is NULL,
9262 * then it's the first time getopts has been called.
9263 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009264static int
Eric Andersenc470f442003-07-28 09:56:35 +00009265getoptscmd(int argc, char **argv)
9266{
9267 char **optbase;
9268
9269 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009270 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009271 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009272 optbase = shellparam.p;
9273 if (shellparam.optind > shellparam.nparam + 1) {
9274 shellparam.optind = 1;
9275 shellparam.optoff = -1;
9276 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009277 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009278 optbase = &argv[3];
9279 if (shellparam.optind > argc - 2) {
9280 shellparam.optind = 1;
9281 shellparam.optoff = -1;
9282 }
9283 }
9284
9285 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009286 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009287}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009288#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009289
Eric Andersencb57d552001-06-28 07:25:16 +00009290
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009291/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009292
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009293static int tokpushback; /* last token pushed back */
9294#define NEOF ((union node *)&tokpushback)
9295static int parsebackquote; /* nonzero if we are inside backquotes */
9296static int lasttoken; /* last token read */
9297static char *wordtext; /* text of last word returned by readtoken */
9298static struct nodelist *backquotelist;
9299static union node *redirnode;
9300static struct heredoc *heredoc;
9301static int quoteflag; /* set if (part of) last token was quoted */
9302
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009303static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9304static void
9305raise_error_syntax(const char *msg)
9306{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009307 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009308 /* NOTREACHED */
9309}
9310
9311/*
9312 * Called when an unexpected token is read during the parse. The argument
9313 * is the token that is expected, or -1 if more than one type of token can
9314 * occur at this point.
9315 */
9316static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9317static void
9318raise_error_unexpected_syntax(int token)
9319{
9320 char msg[64];
9321 int l;
9322
9323 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9324 if (token >= 0)
9325 sprintf(msg + l, " (expecting %s)", tokname(token));
9326 raise_error_syntax(msg);
9327 /* NOTREACHED */
9328}
Eric Andersencb57d552001-06-28 07:25:16 +00009329
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009330#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009331
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009332struct heredoc {
9333 struct heredoc *next; /* next here document in list */
9334 union node *here; /* redirection node */
9335 char *eofmark; /* string indicating end of input */
9336 int striptabs; /* if set, strip leading tabs */
9337};
Eric Andersencb57d552001-06-28 07:25:16 +00009338
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009339static struct heredoc *heredoclist; /* list of here documents to read */
9340
9341/* parsing is heavily cross-recursive, need these forward decls */
9342static union node *andor(void);
9343static union node *pipeline(void);
9344static union node *parse_command(void);
9345static void parseheredoc(void);
9346static char peektoken(void);
9347static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009348
Eric Andersenc470f442003-07-28 09:56:35 +00009349static union node *
9350list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009351{
9352 union node *n1, *n2, *n3;
9353 int tok;
9354
Eric Andersenc470f442003-07-28 09:56:35 +00009355 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9356 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009357 return NULL;
9358 n1 = NULL;
9359 for (;;) {
9360 n2 = andor();
9361 tok = readtoken();
9362 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009363 if (n2->type == NPIPE) {
9364 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009365 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009366 if (n2->type != NREDIR) {
9367 n3 = stalloc(sizeof(struct nredir));
9368 n3->nredir.n = n2;
9369 n3->nredir.redirect = NULL;
9370 n2 = n3;
9371 }
9372 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009373 }
9374 }
9375 if (n1 == NULL) {
9376 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009377 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009378 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009379 n3->type = NSEMI;
9380 n3->nbinary.ch1 = n1;
9381 n3->nbinary.ch2 = n2;
9382 n1 = n3;
9383 }
9384 switch (tok) {
9385 case TBACKGND:
9386 case TSEMI:
9387 tok = readtoken();
9388 /* fall through */
9389 case TNL:
9390 if (tok == TNL) {
9391 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009392 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009393 return n1;
9394 } else {
9395 tokpushback++;
9396 }
Eric Andersenc470f442003-07-28 09:56:35 +00009397 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009398 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009399 return n1;
9400 break;
9401 case TEOF:
9402 if (heredoclist)
9403 parseheredoc();
9404 else
Eric Andersenc470f442003-07-28 09:56:35 +00009405 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009406 return n1;
9407 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009408 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009409 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009410 tokpushback++;
9411 return n1;
9412 }
9413 }
9414}
9415
Eric Andersenc470f442003-07-28 09:56:35 +00009416static union node *
9417andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009418{
Eric Andersencb57d552001-06-28 07:25:16 +00009419 union node *n1, *n2, *n3;
9420 int t;
9421
Eric Andersencb57d552001-06-28 07:25:16 +00009422 n1 = pipeline();
9423 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009424 t = readtoken();
9425 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009426 t = NAND;
9427 } else if (t == TOR) {
9428 t = NOR;
9429 } else {
9430 tokpushback++;
9431 return n1;
9432 }
Eric Andersenc470f442003-07-28 09:56:35 +00009433 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009434 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009435 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009436 n3->type = t;
9437 n3->nbinary.ch1 = n1;
9438 n3->nbinary.ch2 = n2;
9439 n1 = n3;
9440 }
9441}
9442
Eric Andersenc470f442003-07-28 09:56:35 +00009443static union node *
9444pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009445{
Eric Andersencb57d552001-06-28 07:25:16 +00009446 union node *n1, *n2, *pipenode;
9447 struct nodelist *lp, *prev;
9448 int negate;
9449
9450 negate = 0;
9451 TRACE(("pipeline: entered\n"));
9452 if (readtoken() == TNOT) {
9453 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009454 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009455 } else
9456 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009457 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009458 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009459 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009460 pipenode->type = NPIPE;
9461 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009462 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009463 pipenode->npipe.cmdlist = lp;
9464 lp->n = n1;
9465 do {
9466 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009467 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009468 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009469 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009470 prev->next = lp;
9471 } while (readtoken() == TPIPE);
9472 lp->next = NULL;
9473 n1 = pipenode;
9474 }
9475 tokpushback++;
9476 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009477 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009478 n2->type = NNOT;
9479 n2->nnot.com = n1;
9480 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009481 }
9482 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009483}
9484
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009485static union node *
9486makename(void)
9487{
9488 union node *n;
9489
9490 n = stalloc(sizeof(struct narg));
9491 n->type = NARG;
9492 n->narg.next = NULL;
9493 n->narg.text = wordtext;
9494 n->narg.backquote = backquotelist;
9495 return n;
9496}
9497
9498static void
9499fixredir(union node *n, const char *text, int err)
9500{
9501 TRACE(("Fix redir %s %d\n", text, err));
9502 if (!err)
9503 n->ndup.vname = NULL;
9504
9505 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009506 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009507 else if (LONE_DASH(text))
9508 n->ndup.dupfd = -1;
9509 else {
9510 if (err)
9511 raise_error_syntax("Bad fd number");
9512 n->ndup.vname = makename();
9513 }
9514}
9515
9516/*
9517 * Returns true if the text contains nothing to expand (no dollar signs
9518 * or backquotes).
9519 */
9520static int
9521noexpand(char *text)
9522{
9523 char *p;
9524 char c;
9525
9526 p = text;
9527 while ((c = *p++) != '\0') {
9528 if (c == CTLQUOTEMARK)
9529 continue;
9530 if (c == CTLESC)
9531 p++;
9532 else if (SIT(c, BASESYNTAX) == CCTL)
9533 return 0;
9534 }
9535 return 1;
9536}
9537
9538static void
9539parsefname(void)
9540{
9541 union node *n = redirnode;
9542
9543 if (readtoken() != TWORD)
9544 raise_error_unexpected_syntax(-1);
9545 if (n->type == NHERE) {
9546 struct heredoc *here = heredoc;
9547 struct heredoc *p;
9548 int i;
9549
9550 if (quoteflag == 0)
9551 n->type = NXHERE;
9552 TRACE(("Here document %d\n", n->type));
9553 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9554 raise_error_syntax("Illegal eof marker for << redirection");
9555 rmescapes(wordtext);
9556 here->eofmark = wordtext;
9557 here->next = NULL;
9558 if (heredoclist == NULL)
9559 heredoclist = here;
9560 else {
9561 for (p = heredoclist; p->next; p = p->next);
9562 p->next = here;
9563 }
9564 } else if (n->type == NTOFD || n->type == NFROMFD) {
9565 fixredir(n, wordtext, 0);
9566 } else {
9567 n->nfile.fname = makename();
9568 }
9569}
Eric Andersencb57d552001-06-28 07:25:16 +00009570
Eric Andersenc470f442003-07-28 09:56:35 +00009571static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009572simplecmd(void)
9573{
9574 union node *args, **app;
9575 union node *n = NULL;
9576 union node *vars, **vpp;
9577 union node **rpp, *redir;
9578 int savecheckkwd;
9579
9580 args = NULL;
9581 app = &args;
9582 vars = NULL;
9583 vpp = &vars;
9584 redir = NULL;
9585 rpp = &redir;
9586
9587 savecheckkwd = CHKALIAS;
9588 for (;;) {
9589 checkkwd = savecheckkwd;
9590 switch (readtoken()) {
9591 case TWORD:
9592 n = stalloc(sizeof(struct narg));
9593 n->type = NARG;
9594 n->narg.text = wordtext;
9595 n->narg.backquote = backquotelist;
9596 if (savecheckkwd && isassignment(wordtext)) {
9597 *vpp = n;
9598 vpp = &n->narg.next;
9599 } else {
9600 *app = n;
9601 app = &n->narg.next;
9602 savecheckkwd = 0;
9603 }
9604 break;
9605 case TREDIR:
9606 *rpp = n = redirnode;
9607 rpp = &n->nfile.next;
9608 parsefname(); /* read name of redirection file */
9609 break;
9610 case TLP:
9611 if (args && app == &args->narg.next
9612 && !vars && !redir
9613 ) {
9614 struct builtincmd *bcmd;
9615 const char *name;
9616
9617 /* We have a function */
9618 if (readtoken() != TRP)
9619 raise_error_unexpected_syntax(TRP);
9620 name = n->narg.text;
9621 if (!goodname(name)
9622 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9623 ) {
9624 raise_error_syntax("Bad function name");
9625 }
9626 n->type = NDEFUN;
9627 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9628 n->narg.next = parse_command();
9629 return n;
9630 }
9631 /* fall through */
9632 default:
9633 tokpushback++;
9634 goto out;
9635 }
9636 }
9637 out:
9638 *app = NULL;
9639 *vpp = NULL;
9640 *rpp = NULL;
9641 n = stalloc(sizeof(struct ncmd));
9642 n->type = NCMD;
9643 n->ncmd.args = args;
9644 n->ncmd.assign = vars;
9645 n->ncmd.redirect = redir;
9646 return n;
9647}
9648
9649static union node *
9650parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009651{
Eric Andersencb57d552001-06-28 07:25:16 +00009652 union node *n1, *n2;
9653 union node *ap, **app;
9654 union node *cp, **cpp;
9655 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009656 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009657 int t;
9658
9659 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009660 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009661
Eric Andersencb57d552001-06-28 07:25:16 +00009662 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009663 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009664 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009665 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009666 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009667 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009668 n1->type = NIF;
9669 n1->nif.test = list(0);
9670 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009671 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009672 n1->nif.ifpart = list(0);
9673 n2 = n1;
9674 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009675 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009676 n2 = n2->nif.elsepart;
9677 n2->type = NIF;
9678 n2->nif.test = list(0);
9679 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009680 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009681 n2->nif.ifpart = list(0);
9682 }
9683 if (lasttoken == TELSE)
9684 n2->nif.elsepart = list(0);
9685 else {
9686 n2->nif.elsepart = NULL;
9687 tokpushback++;
9688 }
Eric Andersenc470f442003-07-28 09:56:35 +00009689 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009690 break;
9691 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009692 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009693 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009694 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009695 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009696 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009697 got = readtoken();
9698 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009699 TRACE(("expecting DO got %s %s\n", tokname(got),
9700 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009701 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009702 }
9703 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009704 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009705 break;
9706 }
9707 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009708 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009709 raise_error_syntax("Bad for loop variable");
9710 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009711 n1->type = NFOR;
9712 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009713 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009714 if (readtoken() == TIN) {
9715 app = &ap;
9716 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009717 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009718 n2->type = NARG;
9719 n2->narg.text = wordtext;
9720 n2->narg.backquote = backquotelist;
9721 *app = n2;
9722 app = &n2->narg.next;
9723 }
9724 *app = NULL;
9725 n1->nfor.args = ap;
9726 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009727 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009728 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009729 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009730 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009731 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009732 n2->narg.backquote = NULL;
9733 n2->narg.next = NULL;
9734 n1->nfor.args = n2;
9735 /*
9736 * Newline or semicolon here is optional (but note
9737 * that the original Bourne shell only allowed NL).
9738 */
9739 if (lasttoken != TNL && lasttoken != TSEMI)
9740 tokpushback++;
9741 }
Eric Andersenc470f442003-07-28 09:56:35 +00009742 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009743 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009744 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009745 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009746 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009747 break;
9748 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009749 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009750 n1->type = NCASE;
9751 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009752 raise_error_unexpected_syntax(TWORD);
9753 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009754 n2->type = NARG;
9755 n2->narg.text = wordtext;
9756 n2->narg.backquote = backquotelist;
9757 n2->narg.next = NULL;
9758 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009759 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009760 } while (readtoken() == TNL);
9761 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009762 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009763 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009764 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009765 checkkwd = CHKNL | CHKKWD;
9766 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009767 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009768 if (lasttoken == TLP)
9769 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009770 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009771 cp->type = NCLIST;
9772 app = &cp->nclist.pattern;
9773 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009774 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009775 ap->type = NARG;
9776 ap->narg.text = wordtext;
9777 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009778 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009779 break;
9780 app = &ap->narg.next;
9781 readtoken();
9782 }
9783 ap->narg.next = NULL;
9784 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009785 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009786 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009787
Eric Andersenc470f442003-07-28 09:56:35 +00009788 cpp = &cp->nclist.next;
9789
9790 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009791 t = readtoken();
9792 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009793 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009794 raise_error_unexpected_syntax(TENDCASE);
9795 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009796 }
Eric Andersenc470f442003-07-28 09:56:35 +00009797 }
Eric Andersencb57d552001-06-28 07:25:16 +00009798 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009799 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009800 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009801 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009802 n1->type = NSUBSHELL;
9803 n1->nredir.n = list(0);
9804 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009805 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009806 break;
9807 case TBEGIN:
9808 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009809 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009810 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009811 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009812 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009813 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009814 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009815 }
9816
Eric Andersenc470f442003-07-28 09:56:35 +00009817 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009818 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009819
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009820 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009821 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009822 checkkwd = CHKKWD | CHKALIAS;
9823 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009824 while (readtoken() == TREDIR) {
9825 *rpp = n2 = redirnode;
9826 rpp = &n2->nfile.next;
9827 parsefname();
9828 }
9829 tokpushback++;
9830 *rpp = NULL;
9831 if (redir) {
9832 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009833 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009834 n2->type = NREDIR;
9835 n2->nredir.n = n1;
9836 n1 = n2;
9837 }
9838 n1->nredir.redirect = redir;
9839 }
Eric Andersencb57d552001-06-28 07:25:16 +00009840 return n1;
9841}
9842
Eric Andersencb57d552001-06-28 07:25:16 +00009843/*
9844 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9845 * is not NULL, read a here document. In the latter case, eofmark is the
9846 * word which marks the end of the document and striptabs is true if
9847 * leading tabs should be stripped from the document. The argument firstc
9848 * is the first character of the input token or document.
9849 *
9850 * Because C does not have internal subroutines, I have simulated them
9851 * using goto's to implement the subroutine linkage. The following macros
9852 * will run code that appears at the end of readtoken1.
9853 */
9854
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009855static int parsebackquote; /* nonzero if we are inside backquotes */
9856
Eric Andersen2870d962001-07-02 17:27:21 +00009857#define CHECKEND() {goto checkend; checkend_return:;}
9858#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9859#define PARSESUB() {goto parsesub; parsesub_return:;}
9860#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9861#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9862#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009863
9864static int
Eric Andersenc470f442003-07-28 09:56:35 +00009865readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009866{
Eric Andersencb57d552001-06-28 07:25:16 +00009867 int c = firstc;
9868 char *out;
9869 int len;
9870 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009871 struct nodelist *bqlist = 0;
9872 int quotef = 0;
9873 int dblquote = 0;
9874 int varnest = 0; /* levels of variables expansion */
9875 int arinest = 0; /* levels of arithmetic expansion */
9876 int parenlevel = 0; /* levels of parens in arithmetic */
9877 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9878 int oldstyle = 0;
9879 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009880#if __GNUC__
9881 /* Avoid longjmp clobbering */
9882 (void) &out;
9883 (void) &quotef;
9884 (void) &dblquote;
9885 (void) &varnest;
9886 (void) &arinest;
9887 (void) &parenlevel;
9888 (void) &dqvarnest;
9889 (void) &oldstyle;
9890 (void) &prevsyntax;
9891 (void) &syntax;
9892#endif
9893
9894 startlinno = plinno;
9895 dblquote = 0;
9896 if (syntax == DQSYNTAX)
9897 dblquote = 1;
9898 quotef = 0;
9899 bqlist = NULL;
9900 varnest = 0;
9901 arinest = 0;
9902 parenlevel = 0;
9903 dqvarnest = 0;
9904
9905 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009906 loop: { /* for each line, until end of word */
9907 CHECKEND(); /* set c to PEOF if at end of here document */
9908 for (;;) { /* until end of line or end of word */
9909 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009910 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009911 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009912 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009913 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009914 USTPUTC(c, out);
9915 plinno++;
9916 if (doprompt)
9917 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009918 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009919 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009920 case CWORD:
9921 USTPUTC(c, out);
9922 break;
9923 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009924 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009925 USTPUTC(CTLESC, out);
9926 USTPUTC(c, out);
9927 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009928 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009929 c = pgetc2();
9930 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009931 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009932 USTPUTC('\\', out);
9933 pungetc();
9934 } else if (c == '\n') {
9935 if (doprompt)
9936 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009937 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009938 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009939 c != '\\' && c != '`' &&
9940 c != '$' && (
9941 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009942 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009943 ) {
9944 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009945 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009946 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009947 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009948 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009949 USTPUTC(c, out);
9950 quotef++;
9951 }
9952 break;
9953 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009954 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009955 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009956 if (eofmark == NULL) {
9957 USTPUTC(CTLQUOTEMARK, out);
9958 }
Eric Andersencb57d552001-06-28 07:25:16 +00009959 break;
9960 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009961 syntax = DQSYNTAX;
9962 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009963 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009964 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009965 if (eofmark != NULL && arinest == 0
9966 && varnest == 0
9967 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009968 USTPUTC(c, out);
9969 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009970 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009971 syntax = BASESYNTAX;
9972 dblquote = 0;
9973 }
9974 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +00009975 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009976 }
9977 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009978 case CVAR: /* '$' */
9979 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +00009980 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009981 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +00009982 if (varnest > 0) {
9983 varnest--;
9984 if (dqvarnest > 0) {
9985 dqvarnest--;
9986 }
9987 USTPUTC(CTLENDVAR, out);
9988 } else {
9989 USTPUTC(c, out);
9990 }
9991 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009992#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00009993 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009994 parenlevel++;
9995 USTPUTC(c, out);
9996 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009997 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009998 if (parenlevel > 0) {
9999 USTPUTC(c, out);
10000 --parenlevel;
10001 } else {
10002 if (pgetc() == ')') {
10003 if (--arinest == 0) {
10004 USTPUTC(CTLENDARI, out);
10005 syntax = prevsyntax;
10006 if (syntax == DQSYNTAX)
10007 dblquote = 1;
10008 else
10009 dblquote = 0;
10010 } else
10011 USTPUTC(')', out);
10012 } else {
10013 /*
10014 * unbalanced parens
10015 * (don't 2nd guess - no error)
10016 */
10017 pungetc();
10018 USTPUTC(')', out);
10019 }
10020 }
10021 break;
10022#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010023 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010024 PARSEBACKQOLD();
10025 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010026 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010027 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010028 case CIGN:
10029 break;
10030 default:
10031 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010032 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010033#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010034 if (c != PEOA)
10035#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010036 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010037
Eric Andersencb57d552001-06-28 07:25:16 +000010038 }
10039 c = pgetc_macro();
10040 }
10041 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010042 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010043#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010044 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010045 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010046#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010047 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010048 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010049 if (varnest != 0) {
10050 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010051 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010052 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010053 }
10054 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010055 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010056 out = stackblock();
10057 if (eofmark == NULL) {
10058 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010059 && quotef == 0
10060 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010061 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010062 PARSEREDIR();
10063 return lasttoken = TREDIR;
10064 } else {
10065 pungetc();
10066 }
10067 }
10068 quoteflag = quotef;
10069 backquotelist = bqlist;
10070 grabstackblock(len);
10071 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010072 lasttoken = TWORD;
10073 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010074/* end of readtoken routine */
10075
Eric Andersencb57d552001-06-28 07:25:16 +000010076/*
10077 * Check to see whether we are at the end of the here document. When this
10078 * is called, c is set to the first character of the next input line. If
10079 * we are at the end of the here document, this routine sets the c to PEOF.
10080 */
Eric Andersenc470f442003-07-28 09:56:35 +000010081checkend: {
10082 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010083#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010084 if (c == PEOA) {
10085 c = pgetc2();
10086 }
10087#endif
10088 if (striptabs) {
10089 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010090 c = pgetc2();
10091 }
Eric Andersenc470f442003-07-28 09:56:35 +000010092 }
10093 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010094 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010095 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010096
Eric Andersenc470f442003-07-28 09:56:35 +000010097 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010098 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010099 if (*p == '\n' && *q == '\0') {
10100 c = PEOF;
10101 plinno++;
10102 needprompt = doprompt;
10103 } else {
10104 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010105 }
10106 }
10107 }
10108 }
Eric Andersenc470f442003-07-28 09:56:35 +000010109 goto checkend_return;
10110}
Eric Andersencb57d552001-06-28 07:25:16 +000010111
Eric Andersencb57d552001-06-28 07:25:16 +000010112/*
10113 * Parse a redirection operator. The variable "out" points to a string
10114 * specifying the fd to be redirected. The variable "c" contains the
10115 * first character of the redirection operator.
10116 */
Eric Andersenc470f442003-07-28 09:56:35 +000010117parseredir: {
10118 char fd = *out;
10119 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010120
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010121 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010122 if (c == '>') {
10123 np->nfile.fd = 1;
10124 c = pgetc();
10125 if (c == '>')
10126 np->type = NAPPEND;
10127 else if (c == '|')
10128 np->type = NCLOBBER;
10129 else if (c == '&')
10130 np->type = NTOFD;
10131 else {
10132 np->type = NTO;
10133 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010134 }
Eric Andersenc470f442003-07-28 09:56:35 +000010135 } else { /* c == '<' */
10136 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010137 c = pgetc();
10138 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010139 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010140 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010141 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010142 np->nfile.fd = 0;
10143 }
10144 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010145 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010146 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010147 c = pgetc();
10148 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010149 heredoc->striptabs = 1;
10150 } else {
10151 heredoc->striptabs = 0;
10152 pungetc();
10153 }
10154 break;
10155
10156 case '&':
10157 np->type = NFROMFD;
10158 break;
10159
10160 case '>':
10161 np->type = NFROMTO;
10162 break;
10163
10164 default:
10165 np->type = NFROM;
10166 pungetc();
10167 break;
10168 }
Eric Andersencb57d552001-06-28 07:25:16 +000010169 }
Eric Andersenc470f442003-07-28 09:56:35 +000010170 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010171 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010172 redirnode = np;
10173 goto parseredir_return;
10174}
Eric Andersencb57d552001-06-28 07:25:16 +000010175
Eric Andersencb57d552001-06-28 07:25:16 +000010176/*
10177 * Parse a substitution. At this point, we have read the dollar sign
10178 * and nothing else.
10179 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010180
10181/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10182 * (assuming ascii char codes, as the original implementation did) */
10183#define is_special(c) \
10184 ((((unsigned int)c) - 33 < 32) \
10185 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010186parsesub: {
10187 int subtype;
10188 int typeloc;
10189 int flags;
10190 char *p;
10191 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010192
Eric Andersenc470f442003-07-28 09:56:35 +000010193 c = pgetc();
10194 if (
10195 c <= PEOA_OR_PEOF ||
10196 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10197 ) {
10198 USTPUTC('$', out);
10199 pungetc();
10200 } else if (c == '(') { /* $(command) or $((arith)) */
10201 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010202#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010203 PARSEARITH();
10204#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010205 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010206#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010207 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010208 pungetc();
10209 PARSEBACKQNEW();
10210 }
10211 } else {
10212 USTPUTC(CTLVAR, out);
10213 typeloc = out - (char *)stackblock();
10214 USTPUTC(VSNORMAL, out);
10215 subtype = VSNORMAL;
10216 if (c == '{') {
10217 c = pgetc();
10218 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010219 c = pgetc();
10220 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010221 c = '#';
10222 else
10223 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010224 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010225 subtype = 0;
10226 }
10227 if (c > PEOA_OR_PEOF && is_name(c)) {
10228 do {
10229 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010230 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010231 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010232 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010233 do {
10234 STPUTC(c, out);
10235 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010236 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010237 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010238 USTPUTC(c, out);
10239 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010240 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010241 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010242
Eric Andersenc470f442003-07-28 09:56:35 +000010243 STPUTC('=', out);
10244 flags = 0;
10245 if (subtype == 0) {
10246 switch (c) {
10247 case ':':
10248 flags = VSNUL;
10249 c = pgetc();
10250 /*FALLTHROUGH*/
10251 default:
10252 p = strchr(types, c);
10253 if (p == NULL)
10254 goto badsub;
10255 subtype = p - types + VSNORMAL;
10256 break;
10257 case '%':
10258 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010259 {
10260 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010261 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010262 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010263 c = pgetc();
10264 if (c == cc)
10265 subtype++;
10266 else
10267 pungetc();
10268 break;
10269 }
10270 }
Eric Andersenc470f442003-07-28 09:56:35 +000010271 } else {
10272 pungetc();
10273 }
10274 if (dblquote || arinest)
10275 flags |= VSQUOTE;
10276 *((char *)stackblock() + typeloc) = subtype | flags;
10277 if (subtype != VSNORMAL) {
10278 varnest++;
10279 if (dblquote || arinest) {
10280 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010281 }
10282 }
10283 }
Eric Andersenc470f442003-07-28 09:56:35 +000010284 goto parsesub_return;
10285}
Eric Andersencb57d552001-06-28 07:25:16 +000010286
Eric Andersencb57d552001-06-28 07:25:16 +000010287/*
10288 * Called to parse command substitutions. Newstyle is set if the command
10289 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10290 * list of commands (passed by reference), and savelen is the number of
10291 * characters on the top of the stack which must be preserved.
10292 */
Eric Andersenc470f442003-07-28 09:56:35 +000010293parsebackq: {
10294 struct nodelist **nlpp;
10295 int savepbq;
10296 union node *n;
10297 char *volatile str;
10298 struct jmploc jmploc;
10299 struct jmploc *volatile savehandler;
10300 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010301 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010302#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010303 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010304#endif
10305
Eric Andersenc470f442003-07-28 09:56:35 +000010306 savepbq = parsebackquote;
10307 if (setjmp(jmploc.loc)) {
10308 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010309 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010310 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010311 exception_handler = savehandler;
10312 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010313 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010314 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010315 str = NULL;
10316 savelen = out - (char *)stackblock();
10317 if (savelen > 0) {
10318 str = ckmalloc(savelen);
10319 memcpy(str, stackblock(), savelen);
10320 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010321 savehandler = exception_handler;
10322 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010323 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010324 if (oldstyle) {
10325 /* We must read until the closing backquote, giving special
10326 treatment to some slashes, and then push the string and
10327 reread it as input, interpreting it normally. */
10328 char *pout;
10329 int pc;
10330 size_t psavelen;
10331 char *pstr;
10332
10333
10334 STARTSTACKSTR(pout);
10335 for (;;) {
10336 if (needprompt) {
10337 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010338 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010339 pc = pgetc();
10340 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010341 case '`':
10342 goto done;
10343
10344 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010345 pc = pgetc();
10346 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010347 plinno++;
10348 if (doprompt)
10349 setprompt(2);
10350 /*
10351 * If eating a newline, avoid putting
10352 * the newline into the new character
10353 * stream (via the STPUTC after the
10354 * switch).
10355 */
10356 continue;
10357 }
10358 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010359 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010360 STPUTC('\\', pout);
10361 if (pc > PEOA_OR_PEOF) {
10362 break;
10363 }
10364 /* fall through */
10365
10366 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010367#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010368 case PEOA:
10369#endif
10370 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010371 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010372
10373 case '\n':
10374 plinno++;
10375 needprompt = doprompt;
10376 break;
10377
10378 default:
10379 break;
10380 }
10381 STPUTC(pc, pout);
10382 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010383 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010384 STPUTC('\0', pout);
10385 psavelen = pout - (char *)stackblock();
10386 if (psavelen > 0) {
10387 pstr = grabstackstr(pout);
10388 setinputstring(pstr);
10389 }
10390 }
10391 nlpp = &bqlist;
10392 while (*nlpp)
10393 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010394 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010395 (*nlpp)->next = NULL;
10396 parsebackquote = oldstyle;
10397
10398 if (oldstyle) {
10399 saveprompt = doprompt;
10400 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010401 }
10402
Eric Andersenc470f442003-07-28 09:56:35 +000010403 n = list(2);
10404
10405 if (oldstyle)
10406 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010407 else if (readtoken() != TRP)
10408 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010409
10410 (*nlpp)->n = n;
10411 if (oldstyle) {
10412 /*
10413 * Start reading from old file again, ignoring any pushed back
10414 * tokens left from the backquote parsing
10415 */
10416 popfile();
10417 tokpushback = 0;
10418 }
10419 while (stackblocksize() <= savelen)
10420 growstackblock();
10421 STARTSTACKSTR(out);
10422 if (str) {
10423 memcpy(out, str, savelen);
10424 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010425 INT_OFF;
10426 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010427 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010428 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010429 }
10430 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010431 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010432 if (arinest || dblquote)
10433 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10434 else
10435 USTPUTC(CTLBACKQ, out);
10436 if (oldstyle)
10437 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010438 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010439}
10440
Denis Vlasenko131ae172007-02-18 13:00:19 +000010441#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010442/*
10443 * Parse an arithmetic expansion (indicate start of one and set state)
10444 */
Eric Andersenc470f442003-07-28 09:56:35 +000010445parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010446 if (++arinest == 1) {
10447 prevsyntax = syntax;
10448 syntax = ARISYNTAX;
10449 USTPUTC(CTLARI, out);
10450 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010451 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010452 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010453 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010454 } else {
10455 /*
10456 * we collapse embedded arithmetic expansion to
10457 * parenthesis, which should be equivalent
10458 */
10459 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010460 }
Eric Andersenc470f442003-07-28 09:56:35 +000010461 goto parsearith_return;
10462}
10463#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010464
Eric Andersenc470f442003-07-28 09:56:35 +000010465} /* end of readtoken */
10466
Eric Andersencb57d552001-06-28 07:25:16 +000010467/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010468 * Read the next input token.
10469 * If the token is a word, we set backquotelist to the list of cmds in
10470 * backquotes. We set quoteflag to true if any part of the word was
10471 * quoted.
10472 * If the token is TREDIR, then we set redirnode to a structure containing
10473 * the redirection.
10474 * In all cases, the variable startlinno is set to the number of the line
10475 * on which the token starts.
10476 *
10477 * [Change comment: here documents and internal procedures]
10478 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10479 * word parsing code into a separate routine. In this case, readtoken
10480 * doesn't need to have any internal procedures, but parseword does.
10481 * We could also make parseoperator in essence the main routine, and
10482 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010483 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010484#define NEW_xxreadtoken
10485#ifdef NEW_xxreadtoken
10486/* singles must be first! */
10487static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010488
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010489static const char xxreadtoken_tokens[] = {
10490 TNL, TLP, TRP, /* only single occurrence allowed */
10491 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10492 TEOF, /* corresponds to trailing nul */
10493 TAND, TOR, TENDCASE, /* if double occurrence */
10494};
10495
10496#define xxreadtoken_doubles \
10497 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10498#define xxreadtoken_singles \
10499 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10500
10501static int
10502xxreadtoken(void)
10503{
10504 int c;
10505
10506 if (tokpushback) {
10507 tokpushback = 0;
10508 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010509 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010510 if (needprompt) {
10511 setprompt(2);
10512 }
10513 startlinno = plinno;
10514 for (;;) { /* until token or start of word found */
10515 c = pgetc_macro();
10516
10517 if ((c != ' ') && (c != '\t')
10518#if ENABLE_ASH_ALIAS
10519 && (c != PEOA)
10520#endif
10521 ) {
10522 if (c == '#') {
10523 while ((c = pgetc()) != '\n' && c != PEOF);
10524 pungetc();
10525 } else if (c == '\\') {
10526 if (pgetc() != '\n') {
10527 pungetc();
10528 goto READTOKEN1;
10529 }
10530 startlinno = ++plinno;
10531 if (doprompt)
10532 setprompt(2);
10533 } else {
10534 const char *p
10535 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10536
10537 if (c != PEOF) {
10538 if (c == '\n') {
10539 plinno++;
10540 needprompt = doprompt;
10541 }
10542
10543 p = strchr(xxreadtoken_chars, c);
10544 if (p == NULL) {
10545 READTOKEN1:
10546 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10547 }
10548
10549 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10550 if (pgetc() == *p) { /* double occurrence? */
10551 p += xxreadtoken_doubles + 1;
10552 } else {
10553 pungetc();
10554 }
10555 }
10556 }
10557 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10558 }
10559 }
10560 } /* for */
10561}
10562#else
10563#define RETURN(token) return lasttoken = token
10564static int
10565xxreadtoken(void)
10566{
10567 int c;
10568
10569 if (tokpushback) {
10570 tokpushback = 0;
10571 return lasttoken;
10572 }
10573 if (needprompt) {
10574 setprompt(2);
10575 }
10576 startlinno = plinno;
10577 for (;;) { /* until token or start of word found */
10578 c = pgetc_macro();
10579 switch (c) {
10580 case ' ': case '\t':
10581#if ENABLE_ASH_ALIAS
10582 case PEOA:
10583#endif
10584 continue;
10585 case '#':
10586 while ((c = pgetc()) != '\n' && c != PEOF);
10587 pungetc();
10588 continue;
10589 case '\\':
10590 if (pgetc() == '\n') {
10591 startlinno = ++plinno;
10592 if (doprompt)
10593 setprompt(2);
10594 continue;
10595 }
10596 pungetc();
10597 goto breakloop;
10598 case '\n':
10599 plinno++;
10600 needprompt = doprompt;
10601 RETURN(TNL);
10602 case PEOF:
10603 RETURN(TEOF);
10604 case '&':
10605 if (pgetc() == '&')
10606 RETURN(TAND);
10607 pungetc();
10608 RETURN(TBACKGND);
10609 case '|':
10610 if (pgetc() == '|')
10611 RETURN(TOR);
10612 pungetc();
10613 RETURN(TPIPE);
10614 case ';':
10615 if (pgetc() == ';')
10616 RETURN(TENDCASE);
10617 pungetc();
10618 RETURN(TSEMI);
10619 case '(':
10620 RETURN(TLP);
10621 case ')':
10622 RETURN(TRP);
10623 default:
10624 goto breakloop;
10625 }
10626 }
10627 breakloop:
10628 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10629#undef RETURN
10630}
10631#endif /* NEW_xxreadtoken */
10632
10633static int
10634readtoken(void)
10635{
10636 int t;
10637#if DEBUG
10638 int alreadyseen = tokpushback;
10639#endif
10640
10641#if ENABLE_ASH_ALIAS
10642 top:
10643#endif
10644
10645 t = xxreadtoken();
10646
10647 /*
10648 * eat newlines
10649 */
10650 if (checkkwd & CHKNL) {
10651 while (t == TNL) {
10652 parseheredoc();
10653 t = xxreadtoken();
10654 }
10655 }
10656
10657 if (t != TWORD || quoteflag) {
10658 goto out;
10659 }
10660
10661 /*
10662 * check for keywords
10663 */
10664 if (checkkwd & CHKKWD) {
10665 const char *const *pp;
10666
10667 pp = findkwd(wordtext);
10668 if (pp) {
10669 lasttoken = t = pp - tokname_array;
10670 TRACE(("keyword %s recognized\n", tokname(t)));
10671 goto out;
10672 }
10673 }
10674
10675 if (checkkwd & CHKALIAS) {
10676#if ENABLE_ASH_ALIAS
10677 struct alias *ap;
10678 ap = lookupalias(wordtext, 1);
10679 if (ap != NULL) {
10680 if (*ap->val) {
10681 pushstring(ap->val, ap);
10682 }
10683 goto top;
10684 }
10685#endif
10686 }
10687 out:
10688 checkkwd = 0;
10689#if DEBUG
10690 if (!alreadyseen)
10691 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10692 else
10693 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10694#endif
10695 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010696}
10697
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010698static char
10699peektoken(void)
10700{
10701 int t;
10702
10703 t = readtoken();
10704 tokpushback++;
10705 return tokname_array[t][0];
10706}
Eric Andersencb57d552001-06-28 07:25:16 +000010707
10708/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010709 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10710 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010711 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010712static union node *
10713parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010714{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010715 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010716
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010717 tokpushback = 0;
10718 doprompt = interact;
10719 if (doprompt)
10720 setprompt(doprompt);
10721 needprompt = 0;
10722 t = readtoken();
10723 if (t == TEOF)
10724 return NEOF;
10725 if (t == TNL)
10726 return NULL;
10727 tokpushback++;
10728 return list(1);
10729}
10730
10731/*
10732 * Input any here documents.
10733 */
10734static void
10735parseheredoc(void)
10736{
10737 struct heredoc *here;
10738 union node *n;
10739
10740 here = heredoclist;
10741 heredoclist = 0;
10742
10743 while (here) {
10744 if (needprompt) {
10745 setprompt(2);
10746 }
10747 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10748 here->eofmark, here->striptabs);
10749 n = stalloc(sizeof(struct narg));
10750 n->narg.type = NARG;
10751 n->narg.next = NULL;
10752 n->narg.text = wordtext;
10753 n->narg.backquote = backquotelist;
10754 here->here->nhere.doc = n;
10755 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010756 }
Eric Andersencb57d552001-06-28 07:25:16 +000010757}
10758
10759
10760/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010761 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010762 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010763#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010764static const char *
10765expandstr(const char *ps)
10766{
10767 union node n;
10768
10769 /* XXX Fix (char *) cast. */
10770 setinputstring((char *)ps);
10771 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10772 popfile();
10773
10774 n.narg.type = NARG;
10775 n.narg.next = NULL;
10776 n.narg.text = wordtext;
10777 n.narg.backquote = backquotelist;
10778
10779 expandarg(&n, NULL, 0);
10780 return stackblock();
10781}
10782#endif
10783
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010784/*
10785 * Execute a command or commands contained in a string.
10786 */
10787static int
10788evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010789{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010790 union node *n;
10791 struct stackmark smark;
10792 int skip;
10793
10794 setinputstring(s);
10795 setstackmark(&smark);
10796
10797 skip = 0;
10798 while ((n = parsecmd(0)) != NEOF) {
10799 evaltree(n, 0);
10800 popstackmark(&smark);
10801 skip = evalskip;
10802 if (skip)
10803 break;
10804 }
10805 popfile();
10806
10807 skip &= mask;
10808 evalskip = skip;
10809 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010810}
10811
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010812/*
10813 * The eval command.
10814 */
10815static int
10816evalcmd(int argc, char **argv)
10817{
10818 char *p;
10819 char *concat;
10820 char **ap;
10821
10822 if (argc > 1) {
10823 p = argv[1];
10824 if (argc > 2) {
10825 STARTSTACKSTR(concat);
10826 ap = argv + 2;
10827 for (;;) {
10828 concat = stack_putstr(p, concat);
10829 p = *ap++;
10830 if (p == NULL)
10831 break;
10832 STPUTC(' ', concat);
10833 }
10834 STPUTC('\0', concat);
10835 p = grabstackstr(concat);
10836 }
10837 evalstring(p, ~SKIPEVAL);
10838
10839 }
10840 return exitstatus;
10841}
10842
10843/*
10844 * Read and execute commands. "Top" is nonzero for the top level command
10845 * loop; it turns on prompting if the shell is interactive.
10846 */
10847static int
10848cmdloop(int top)
10849{
10850 union node *n;
10851 struct stackmark smark;
10852 int inter;
10853 int numeof = 0;
10854
10855 TRACE(("cmdloop(%d) called\n", top));
10856 for (;;) {
10857 int skip;
10858
10859 setstackmark(&smark);
10860#if JOBS
10861 if (jobctl)
10862 showjobs(stderr, SHOW_CHANGED);
10863#endif
10864 inter = 0;
10865 if (iflag && top) {
10866 inter++;
10867#if ENABLE_ASH_MAIL
10868 chkmail();
10869#endif
10870 }
10871 n = parsecmd(inter);
10872 /* showtree(n); DEBUG */
10873 if (n == NEOF) {
10874 if (!top || numeof >= 50)
10875 break;
10876 if (!stoppedjobs()) {
10877 if (!Iflag)
10878 break;
10879 out2str("\nUse \"exit\" to leave shell.\n");
10880 }
10881 numeof++;
10882 } else if (nflag == 0) {
10883 job_warning = (job_warning == 2) ? 1 : 0;
10884 numeof = 0;
10885 evaltree(n, 0);
10886 }
10887 popstackmark(&smark);
10888 skip = evalskip;
10889
10890 if (skip) {
10891 evalskip = 0;
10892 return skip & SKIPEVAL;
10893 }
10894 }
10895 return 0;
10896}
10897
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010898/*
10899 * Take commands from a file. To be compatible we should do a path
10900 * search for the file, which is necessary to find sub-commands.
10901 */
10902static char *
10903find_dot_file(char *name)
10904{
10905 char *fullname;
10906 const char *path = pathval();
10907 struct stat statb;
10908
10909 /* don't try this for absolute or relative paths */
10910 if (strchr(name, '/'))
10911 return name;
10912
10913 while ((fullname = padvance(&path, name)) != NULL) {
10914 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10915 /*
10916 * Don't bother freeing here, since it will
10917 * be freed by the caller.
10918 */
10919 return fullname;
10920 }
10921 stunalloc(fullname);
10922 }
10923
10924 /* not found in the PATH */
10925 ash_msg_and_raise_error("%s: not found", name);
10926 /* NOTREACHED */
10927}
10928
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010929static int
10930dotcmd(int argc, char **argv)
10931{
10932 struct strlist *sp;
10933 volatile struct shparam saveparam;
10934 int status = 0;
10935
10936 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000010937 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010938
10939 if (argc >= 2) { /* That's what SVR2 does */
10940 char *fullname;
10941
10942 fullname = find_dot_file(argv[1]);
10943
10944 if (argc > 2) {
10945 saveparam = shellparam;
10946 shellparam.malloc = 0;
10947 shellparam.nparam = argc - 2;
10948 shellparam.p = argv + 2;
10949 };
10950
10951 setinputfile(fullname, INPUT_PUSH_FILE);
10952 commandname = fullname;
10953 cmdloop(0);
10954 popfile();
10955
10956 if (argc > 2) {
10957 freeparam(&shellparam);
10958 shellparam = saveparam;
10959 };
10960 status = exitstatus;
10961 }
10962 return status;
10963}
10964
10965static int
10966exitcmd(int argc, char **argv)
10967{
10968 if (stoppedjobs())
10969 return 0;
10970 if (argc > 1)
10971 exitstatus = number(argv[1]);
10972 raise_exception(EXEXIT);
10973 /* NOTREACHED */
10974}
10975
10976#if ENABLE_ASH_BUILTIN_ECHO
10977static int
10978echocmd(int argc, char **argv)
10979{
10980 return bb_echo(argv);
10981}
10982#endif
10983
10984#if ENABLE_ASH_BUILTIN_TEST
10985static int
10986testcmd(int argc, char **argv)
10987{
10988 return bb_test(argc, argv);
10989}
10990#endif
10991
10992/*
10993 * Read a file containing shell functions.
10994 */
10995static void
10996readcmdfile(char *name)
10997{
10998 setinputfile(name, INPUT_PUSH_FILE);
10999 cmdloop(0);
11000 popfile();
11001}
11002
11003
Denis Vlasenkocc571512007-02-23 21:10:35 +000011004/* ============ find_command inplementation */
11005
11006/*
11007 * Resolve a command name. If you change this routine, you may have to
11008 * change the shellexec routine as well.
11009 */
11010static void
11011find_command(char *name, struct cmdentry *entry, int act, const char *path)
11012{
11013 struct tblentry *cmdp;
11014 int idx;
11015 int prev;
11016 char *fullname;
11017 struct stat statb;
11018 int e;
11019 int updatetbl;
11020 struct builtincmd *bcmd;
11021
11022 /* If name contains a slash, don't use PATH or hash table */
11023 if (strchr(name, '/') != NULL) {
11024 entry->u.index = -1;
11025 if (act & DO_ABS) {
11026 while (stat(name, &statb) < 0) {
11027#ifdef SYSV
11028 if (errno == EINTR)
11029 continue;
11030#endif
11031 entry->cmdtype = CMDUNKNOWN;
11032 return;
11033 }
11034 }
11035 entry->cmdtype = CMDNORMAL;
11036 return;
11037 }
11038
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011039/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011040
11041 updatetbl = (path == pathval());
11042 if (!updatetbl) {
11043 act |= DO_ALTPATH;
11044 if (strstr(path, "%builtin") != NULL)
11045 act |= DO_ALTBLTIN;
11046 }
11047
11048 /* If name is in the table, check answer will be ok */
11049 cmdp = cmdlookup(name, 0);
11050 if (cmdp != NULL) {
11051 int bit;
11052
11053 switch (cmdp->cmdtype) {
11054 default:
11055#if DEBUG
11056 abort();
11057#endif
11058 case CMDNORMAL:
11059 bit = DO_ALTPATH;
11060 break;
11061 case CMDFUNCTION:
11062 bit = DO_NOFUNC;
11063 break;
11064 case CMDBUILTIN:
11065 bit = DO_ALTBLTIN;
11066 break;
11067 }
11068 if (act & bit) {
11069 updatetbl = 0;
11070 cmdp = NULL;
11071 } else if (cmdp->rehash == 0)
11072 /* if not invalidated by cd, we're done */
11073 goto success;
11074 }
11075
11076 /* If %builtin not in path, check for builtin next */
11077 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011078 if (bcmd) {
11079 if (IS_BUILTIN_REGULAR(bcmd))
11080 goto builtin_success;
11081 if (act & DO_ALTPATH) {
11082 if (!(act & DO_ALTBLTIN))
11083 goto builtin_success;
11084 } else if (builtinloc <= 0) {
11085 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011086 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011087 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011088
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011089#if ENABLE_FEATURE_SH_STANDALONE
11090 if (find_applet_by_name(name)) {
11091 entry->cmdtype = CMDNORMAL;
11092 entry->u.index = -1;
11093 return;
11094 }
11095#endif
11096
Denis Vlasenkocc571512007-02-23 21:10:35 +000011097 /* We have to search path. */
11098 prev = -1; /* where to start */
11099 if (cmdp && cmdp->rehash) { /* doing a rehash */
11100 if (cmdp->cmdtype == CMDBUILTIN)
11101 prev = builtinloc;
11102 else
11103 prev = cmdp->param.index;
11104 }
11105
11106 e = ENOENT;
11107 idx = -1;
11108 loop:
11109 while ((fullname = padvance(&path, name)) != NULL) {
11110 stunalloc(fullname);
11111 idx++;
11112 if (pathopt) {
11113 if (prefix(pathopt, "builtin")) {
11114 if (bcmd)
11115 goto builtin_success;
11116 continue;
11117 } else if (!(act & DO_NOFUNC) &&
11118 prefix(pathopt, "func")) {
11119 /* handled below */
11120 } else {
11121 /* ignore unimplemented options */
11122 continue;
11123 }
11124 }
11125 /* if rehash, don't redo absolute path names */
11126 if (fullname[0] == '/' && idx <= prev) {
11127 if (idx < prev)
11128 continue;
11129 TRACE(("searchexec \"%s\": no change\n", name));
11130 goto success;
11131 }
11132 while (stat(fullname, &statb) < 0) {
11133#ifdef SYSV
11134 if (errno == EINTR)
11135 continue;
11136#endif
11137 if (errno != ENOENT && errno != ENOTDIR)
11138 e = errno;
11139 goto loop;
11140 }
11141 e = EACCES; /* if we fail, this will be the error */
11142 if (!S_ISREG(statb.st_mode))
11143 continue;
11144 if (pathopt) { /* this is a %func directory */
11145 stalloc(strlen(fullname) + 1);
11146 readcmdfile(fullname);
11147 cmdp = cmdlookup(name, 0);
11148 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11149 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11150 stunalloc(fullname);
11151 goto success;
11152 }
11153 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11154 if (!updatetbl) {
11155 entry->cmdtype = CMDNORMAL;
11156 entry->u.index = idx;
11157 return;
11158 }
11159 INT_OFF;
11160 cmdp = cmdlookup(name, 1);
11161 cmdp->cmdtype = CMDNORMAL;
11162 cmdp->param.index = idx;
11163 INT_ON;
11164 goto success;
11165 }
11166
11167 /* We failed. If there was an entry for this command, delete it */
11168 if (cmdp && updatetbl)
11169 delete_cmd_entry();
11170 if (act & DO_ERR)
11171 ash_msg("%s: %s", name, errmsg(e, "not found"));
11172 entry->cmdtype = CMDUNKNOWN;
11173 return;
11174
11175 builtin_success:
11176 if (!updatetbl) {
11177 entry->cmdtype = CMDBUILTIN;
11178 entry->u.cmd = bcmd;
11179 return;
11180 }
11181 INT_OFF;
11182 cmdp = cmdlookup(name, 1);
11183 cmdp->cmdtype = CMDBUILTIN;
11184 cmdp->param.cmd = bcmd;
11185 INT_ON;
11186 success:
11187 cmdp->rehash = 0;
11188 entry->cmdtype = cmdp->cmdtype;
11189 entry->u = cmdp->param;
11190}
11191
11192
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011193/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011194
Eric Andersencb57d552001-06-28 07:25:16 +000011195/*
Eric Andersencb57d552001-06-28 07:25:16 +000011196 * The trap builtin.
11197 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011198static int
Eric Andersenc470f442003-07-28 09:56:35 +000011199trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011200{
11201 char *action;
11202 char **ap;
11203 int signo;
11204
Eric Andersenc470f442003-07-28 09:56:35 +000011205 nextopt(nullstr);
11206 ap = argptr;
11207 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011208 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011209 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011210 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011211
Rob Landleyc9c1a412006-07-12 19:17:55 +000011212 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011213 out1fmt("trap -- %s %s\n",
11214 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011215 }
11216 }
11217 return 0;
11218 }
Eric Andersenc470f442003-07-28 09:56:35 +000011219 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011220 action = NULL;
11221 else
11222 action = *ap++;
11223 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011224 signo = get_signum(*ap);
11225 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011226 ash_msg_and_raise_error("%s: bad trap", *ap);
11227 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011228 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011229 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011230 action = NULL;
11231 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011232 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011233 }
Eric Andersenc470f442003-07-28 09:56:35 +000011234 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011235 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011236 trap[signo] = action;
11237 if (signo != 0)
11238 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011239 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011240 ap++;
11241 }
11242 return 0;
11243}
11244
Eric Andersenc470f442003-07-28 09:56:35 +000011245
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011246/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011247
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011248#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011249/*
11250 * Lists available builtins
11251 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011252static int
11253helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011254{
11255 int col, i;
11256
11257 out1fmt("\nBuilt-in commands:\n-------------------\n");
11258 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11259 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011260 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011261 if (col > 60) {
11262 out1fmt("\n");
11263 col = 0;
11264 }
11265 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011266#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011267 for (i = 0; i < NUM_APPLETS; i++) {
11268 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11269 if (col > 60) {
11270 out1fmt("\n");
11271 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011272 }
11273 }
11274#endif
11275 out1fmt("\n\n");
11276 return EXIT_SUCCESS;
11277}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011278#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011279
Eric Andersencb57d552001-06-28 07:25:16 +000011280/*
Eric Andersencb57d552001-06-28 07:25:16 +000011281 * The export and readonly commands.
11282 */
Eric Andersenc470f442003-07-28 09:56:35 +000011283static int
11284exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011285{
11286 struct var *vp;
11287 char *name;
11288 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011289 char **aptr;
11290 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011291
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011292 if (nextopt("p") != 'p') {
11293 aptr = argptr;
11294 name = *aptr;
11295 if (name) {
11296 do {
11297 p = strchr(name, '=');
11298 if (p != NULL) {
11299 p++;
11300 } else {
11301 vp = *findvar(hashvar(name), name);
11302 if (vp) {
11303 vp->flags |= flag;
11304 continue;
11305 }
Eric Andersencb57d552001-06-28 07:25:16 +000011306 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011307 setvar(name, p, flag);
11308 } while ((name = *++aptr) != NULL);
11309 return 0;
11310 }
Eric Andersencb57d552001-06-28 07:25:16 +000011311 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011312 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011313 return 0;
11314}
11315
Eric Andersencb57d552001-06-28 07:25:16 +000011316/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011317 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011318 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011319static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011320unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011321{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011322 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011323
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011324 cmdp = cmdlookup(name, 0);
11325 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11326 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011327}
11328
Eric Andersencb57d552001-06-28 07:25:16 +000011329/*
Eric Andersencb57d552001-06-28 07:25:16 +000011330 * The unset builtin command. We unset the function before we unset the
11331 * variable to allow a function to be unset when there is a readonly variable
11332 * with the same name.
11333 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011334static int
Eric Andersenc470f442003-07-28 09:56:35 +000011335unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011336{
11337 char **ap;
11338 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011339 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011340 int ret = 0;
11341
11342 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011343 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011344 }
Eric Andersencb57d552001-06-28 07:25:16 +000011345
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011346 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011347 if (flag != 'f') {
11348 i = unsetvar(*ap);
11349 ret |= i;
11350 if (!(i & 2))
11351 continue;
11352 }
11353 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011354 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011355 }
Eric Andersenc470f442003-07-28 09:56:35 +000011356 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011357}
11358
11359
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011360/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011361
Eric Andersenc470f442003-07-28 09:56:35 +000011362#include <sys/times.h>
11363
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011364static const unsigned char timescmd_str[] = {
11365 ' ', offsetof(struct tms, tms_utime),
11366 '\n', offsetof(struct tms, tms_stime),
11367 ' ', offsetof(struct tms, tms_cutime),
11368 '\n', offsetof(struct tms, tms_cstime),
11369 0
11370};
Eric Andersencb57d552001-06-28 07:25:16 +000011371
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011372static int
11373timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011374{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011375 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011376 const unsigned char *p;
11377 struct tms buf;
11378
11379 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011380 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011381
11382 p = timescmd_str;
11383 do {
11384 t = *(clock_t *)(((char *) &buf) + p[1]);
11385 s = t / clk_tck;
11386 out1fmt("%ldm%ld.%.3lds%c",
11387 s/60, s%60,
11388 ((t - s * clk_tck) * 1000) / clk_tck,
11389 p[0]);
11390 } while (*(p += 2));
11391
Eric Andersencb57d552001-06-28 07:25:16 +000011392 return 0;
11393}
11394
Denis Vlasenko131ae172007-02-18 13:00:19 +000011395#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011396static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011397dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011398{
Eric Andersened9ecf72004-06-22 08:29:45 +000011399 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011400 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011401
Denis Vlasenkob012b102007-02-19 22:43:01 +000011402 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011403 result = arith(s, &errcode);
11404 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011405 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011406 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011407 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011408 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011409 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011410 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011411 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011412 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011413 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011414
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011415 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011416}
Eric Andersenc470f442003-07-28 09:56:35 +000011417
Eric Andersenc470f442003-07-28 09:56:35 +000011418/*
Eric Andersen90898442003-08-06 11:20:52 +000011419 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11420 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11421 *
11422 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011423 */
11424static int
Eric Andersen90898442003-08-06 11:20:52 +000011425letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011426{
Eric Andersenc470f442003-07-28 09:56:35 +000011427 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011428 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011429
Eric Andersen90898442003-08-06 11:20:52 +000011430 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011431 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011432 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011433 for (ap = argv + 1; *ap; ap++) {
11434 i = dash_arith(*ap);
11435 }
Eric Andersenc470f442003-07-28 09:56:35 +000011436
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011437 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011438}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011439#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011440
Eric Andersenc470f442003-07-28 09:56:35 +000011441
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011442/* ============ miscbltin.c
11443 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011444 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011445 */
11446
11447#undef rflag
11448
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011449#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011450typedef enum __rlimit_resource rlim_t;
11451#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011452
Eric Andersenc470f442003-07-28 09:56:35 +000011453/*
11454 * The read builtin. The -e option causes backslashes to escape the
11455 * following character.
11456 *
11457 * This uses unbuffered input, which may be avoidable in some cases.
11458 */
Eric Andersenc470f442003-07-28 09:56:35 +000011459static int
11460readcmd(int argc, char **argv)
11461{
11462 char **ap;
11463 int backslash;
11464 char c;
11465 int rflag;
11466 char *prompt;
11467 const char *ifs;
11468 char *p;
11469 int startword;
11470 int status;
11471 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011472#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011473 int nch_flag = 0;
11474 int nchars = 0;
11475 int silent = 0;
11476 struct termios tty, old_tty;
11477#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011478#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011479 fd_set set;
11480 struct timeval ts;
11481
11482 ts.tv_sec = ts.tv_usec = 0;
11483#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011484
11485 rflag = 0;
11486 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011487#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011488 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011489#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011490 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011491#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011492 while ((i = nextopt("p:rt:")) != '\0')
11493#else
11494 while ((i = nextopt("p:r")) != '\0')
11495#endif
11496 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011497 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011498 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011499 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011500 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011501#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011502 case 'n':
11503 nchars = strtol(optionarg, &p, 10);
11504 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011505 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011506 nch_flag = (nchars > 0);
11507 break;
11508 case 's':
11509 silent = 1;
11510 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011511#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011512#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011513 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011514 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011515 ts.tv_usec = 0;
11516 if (*p == '.') {
11517 char *p2;
11518 if (*++p) {
11519 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011520 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011521 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011522 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011523 scale = p2 - p;
11524 /* normalize to usec */
11525 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011526 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011527 while (scale++ < 6)
11528 ts.tv_usec *= 10;
11529 }
11530 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011531 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011532 }
11533 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011534 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011535 break;
11536#endif
11537 case 'r':
11538 rflag = 1;
11539 break;
11540 default:
11541 break;
11542 }
Eric Andersenc470f442003-07-28 09:56:35 +000011543 }
11544 if (prompt && isatty(0)) {
11545 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011546 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011547 ap = argptr;
11548 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011549 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011550 ifs = bltinlookup("IFS");
11551 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011552 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011553#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011554 if (nch_flag || silent) {
11555 tcgetattr(0, &tty);
11556 old_tty = tty;
11557 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011558 tty.c_lflag &= ~ICANON;
11559 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011560 }
11561 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011562 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011563
11564 }
11565 tcsetattr(0, TCSANOW, &tty);
11566 }
11567#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011568#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011569 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011570 FD_ZERO(&set);
11571 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011572
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011573 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011574 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011575#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011576 if (nch_flag)
11577 tcsetattr(0, TCSANOW, &old_tty);
11578#endif
11579 return 1;
11580 }
11581 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011582#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011583 status = 0;
11584 startword = 1;
11585 backslash = 0;
11586 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011587#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011588 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011589#else
11590 for (;;)
11591#endif
11592 {
Eric Andersenc470f442003-07-28 09:56:35 +000011593 if (read(0, &c, 1) != 1) {
11594 status = 1;
11595 break;
11596 }
11597 if (c == '\0')
11598 continue;
11599 if (backslash) {
11600 backslash = 0;
11601 if (c != '\n')
11602 goto put;
11603 continue;
11604 }
11605 if (!rflag && c == '\\') {
11606 backslash++;
11607 continue;
11608 }
11609 if (c == '\n')
11610 break;
11611 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11612 continue;
11613 }
11614 startword = 0;
11615 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11616 STACKSTRNUL(p);
11617 setvar(*ap, stackblock(), 0);
11618 ap++;
11619 startword = 1;
11620 STARTSTACKSTR(p);
11621 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011622 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011623 STPUTC(c, p);
11624 }
11625 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011626#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011627 if (nch_flag || silent)
11628 tcsetattr(0, TCSANOW, &old_tty);
11629#endif
11630
Eric Andersenc470f442003-07-28 09:56:35 +000011631 STACKSTRNUL(p);
11632 /* Remove trailing blanks */
11633 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11634 *p = '\0';
11635 setvar(*ap, stackblock(), 0);
11636 while (*++ap != NULL)
11637 setvar(*ap, nullstr, 0);
11638 return status;
11639}
11640
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011641static int
11642umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011643{
11644 static const char permuser[3] = "ugo";
11645 static const char permmode[3] = "rwx";
11646 static const short int permmask[] = {
11647 S_IRUSR, S_IWUSR, S_IXUSR,
11648 S_IRGRP, S_IWGRP, S_IXGRP,
11649 S_IROTH, S_IWOTH, S_IXOTH
11650 };
11651
11652 char *ap;
11653 mode_t mask;
11654 int i;
11655 int symbolic_mode = 0;
11656
11657 while (nextopt("S") != '\0') {
11658 symbolic_mode = 1;
11659 }
11660
Denis Vlasenkob012b102007-02-19 22:43:01 +000011661 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011662 mask = umask(0);
11663 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011664 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011665
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011666 ap = *argptr;
11667 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011668 if (symbolic_mode) {
11669 char buf[18];
11670 char *p = buf;
11671
11672 for (i = 0; i < 3; i++) {
11673 int j;
11674
11675 *p++ = permuser[i];
11676 *p++ = '=';
11677 for (j = 0; j < 3; j++) {
11678 if ((mask & permmask[3 * i + j]) == 0) {
11679 *p++ = permmode[j];
11680 }
11681 }
11682 *p++ = ',';
11683 }
11684 *--p = 0;
11685 puts(buf);
11686 } else {
11687 out1fmt("%.4o\n", mask);
11688 }
11689 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011690 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011691 mask = 0;
11692 do {
11693 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011694 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011695 mask = (mask << 3) + (*ap - '0');
11696 } while (*++ap != '\0');
11697 umask(mask);
11698 } else {
11699 mask = ~mask & 0777;
11700 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011701 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011702 }
11703 umask(~mask & 0777);
11704 }
11705 }
11706 return 0;
11707}
11708
11709/*
11710 * ulimit builtin
11711 *
11712 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11713 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11714 * ash by J.T. Conklin.
11715 *
11716 * Public domain.
11717 */
11718
11719struct limits {
11720 const char *name;
11721 int cmd;
11722 int factor; /* multiply by to get rlim_{cur,max} values */
11723 char option;
11724};
11725
11726static const struct limits limits[] = {
11727#ifdef RLIMIT_CPU
11728 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11729#endif
11730#ifdef RLIMIT_FSIZE
11731 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11732#endif
11733#ifdef RLIMIT_DATA
11734 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11735#endif
11736#ifdef RLIMIT_STACK
11737 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11738#endif
11739#ifdef RLIMIT_CORE
11740 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11741#endif
11742#ifdef RLIMIT_RSS
11743 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11744#endif
11745#ifdef RLIMIT_MEMLOCK
11746 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11747#endif
11748#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011749 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011750#endif
11751#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011752 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011753#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011754#ifdef RLIMIT_AS
11755 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011756#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011757#ifdef RLIMIT_LOCKS
11758 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011759#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011760 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011761};
11762
Glenn L McGrath76620622004-01-13 10:19:37 +000011763enum limtype { SOFT = 0x1, HARD = 0x2 };
11764
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011765static void
11766printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011767 const struct limits *l)
11768{
11769 rlim_t val;
11770
11771 val = limit->rlim_max;
11772 if (how & SOFT)
11773 val = limit->rlim_cur;
11774
11775 if (val == RLIM_INFINITY)
11776 out1fmt("unlimited\n");
11777 else {
11778 val /= l->factor;
11779 out1fmt("%lld\n", (long long) val);
11780 }
11781}
11782
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011783static int
Eric Andersenc470f442003-07-28 09:56:35 +000011784ulimitcmd(int argc, char **argv)
11785{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011786 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011787 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011788 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011789 const struct limits *l;
11790 int set, all = 0;
11791 int optc, what;
11792 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011793
11794 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011795 while ((optc = nextopt("HSa"
11796#ifdef RLIMIT_CPU
11797 "t"
11798#endif
11799#ifdef RLIMIT_FSIZE
11800 "f"
11801#endif
11802#ifdef RLIMIT_DATA
11803 "d"
11804#endif
11805#ifdef RLIMIT_STACK
11806 "s"
11807#endif
11808#ifdef RLIMIT_CORE
11809 "c"
11810#endif
11811#ifdef RLIMIT_RSS
11812 "m"
11813#endif
11814#ifdef RLIMIT_MEMLOCK
11815 "l"
11816#endif
11817#ifdef RLIMIT_NPROC
11818 "p"
11819#endif
11820#ifdef RLIMIT_NOFILE
11821 "n"
11822#endif
11823#ifdef RLIMIT_AS
11824 "v"
11825#endif
11826#ifdef RLIMIT_LOCKS
11827 "w"
11828#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011829 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011830 switch (optc) {
11831 case 'H':
11832 how = HARD;
11833 break;
11834 case 'S':
11835 how = SOFT;
11836 break;
11837 case 'a':
11838 all = 1;
11839 break;
11840 default:
11841 what = optc;
11842 }
11843
Glenn L McGrath76620622004-01-13 10:19:37 +000011844 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011845 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011846
11847 set = *argptr ? 1 : 0;
11848 if (set) {
11849 char *p = *argptr;
11850
11851 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011852 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011853 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011854 val = RLIM_INFINITY;
11855 else {
11856 val = (rlim_t) 0;
11857
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011858 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011859 val = (val * 10) + (long)(c - '0');
11860 if (val < (rlim_t) 0)
11861 break;
11862 }
11863 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011864 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011865 val *= l->factor;
11866 }
11867 }
11868 if (all) {
11869 for (l = limits; l->name; l++) {
11870 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011871 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011872 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011873 }
11874 return 0;
11875 }
11876
11877 getrlimit(l->cmd, &limit);
11878 if (set) {
11879 if (how & HARD)
11880 limit.rlim_max = val;
11881 if (how & SOFT)
11882 limit.rlim_cur = val;
11883 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011884 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011885 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011886 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011887 }
11888 return 0;
11889}
11890
Eric Andersen90898442003-08-06 11:20:52 +000011891
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011892/* ============ Math support */
11893
Denis Vlasenko131ae172007-02-18 13:00:19 +000011894#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011895
11896/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11897
11898 Permission is hereby granted, free of charge, to any person obtaining
11899 a copy of this software and associated documentation files (the
11900 "Software"), to deal in the Software without restriction, including
11901 without limitation the rights to use, copy, modify, merge, publish,
11902 distribute, sublicense, and/or sell copies of the Software, and to
11903 permit persons to whom the Software is furnished to do so, subject to
11904 the following conditions:
11905
11906 The above copyright notice and this permission notice shall be
11907 included in all copies or substantial portions of the Software.
11908
11909 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11910 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11911 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11912 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11913 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11914 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11915 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11916*/
11917
11918/* This is my infix parser/evaluator. It is optimized for size, intended
11919 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011920 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011921 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000011922 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000011923 * be that which POSIX specifies for shells. */
11924
11925/* The code uses a simple two-stack algorithm. See
11926 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000011927 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000011928 * this is based (this code differs in that it applies operators immediately
11929 * to the stack instead of adding them to a queue to end up with an
11930 * expression). */
11931
11932/* To use the routine, call it with an expression string and error return
11933 * pointer */
11934
11935/*
11936 * Aug 24, 2001 Manuel Novoa III
11937 *
11938 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
11939 *
11940 * 1) In arith_apply():
11941 * a) Cached values of *numptr and &(numptr[-1]).
11942 * b) Removed redundant test for zero denominator.
11943 *
11944 * 2) In arith():
11945 * a) Eliminated redundant code for processing operator tokens by moving
11946 * to a table-based implementation. Also folded handling of parens
11947 * into the table.
11948 * b) Combined all 3 loops which called arith_apply to reduce generated
11949 * code size at the cost of speed.
11950 *
11951 * 3) The following expressions were treated as valid by the original code:
11952 * 1() , 0! , 1 ( *3 ) .
11953 * These bugs have been fixed by internally enclosing the expression in
11954 * parens and then checking that all binary ops and right parens are
11955 * preceded by a valid expression (NUM_TOKEN).
11956 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011957 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000011958 * ctype's isspace() if it is used by another busybox applet or if additional
11959 * whitespace chars should be considered. Look below the "#include"s for a
11960 * precompiler test.
11961 */
11962
11963/*
11964 * Aug 26, 2001 Manuel Novoa III
11965 *
11966 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
11967 *
11968 * Merge in Aaron's comments previously posted to the busybox list,
11969 * modified slightly to take account of my changes to the code.
11970 *
11971 */
11972
11973/*
11974 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
11975 *
11976 * - allow access to variable,
11977 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
11978 * - realize assign syntax (VAR=expr, +=, *= etc)
11979 * - realize exponentiation (** operator)
11980 * - realize comma separated - expr, expr
11981 * - realise ++expr --expr expr++ expr--
11982 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000011983 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000011984 * - was restored loses XOR operator
11985 * - remove one goto label, added three ;-)
11986 * - protect $((num num)) as true zero expr (Manuel`s error)
11987 * - always use special isspace(), see comment from bash ;-)
11988 */
11989
Eric Andersen90898442003-08-06 11:20:52 +000011990#define arith_isspace(arithval) \
11991 (arithval == ' ' || arithval == '\n' || arithval == '\t')
11992
Eric Andersen90898442003-08-06 11:20:52 +000011993typedef unsigned char operator;
11994
11995/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000011996 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000011997 * precedence. The ID portion is so that multiple operators can have the
11998 * same precedence, ensuring that the leftmost one is evaluated first.
11999 * Consider * and /. */
12000
12001#define tok_decl(prec,id) (((id)<<5)|(prec))
12002#define PREC(op) ((op) & 0x1F)
12003
12004#define TOK_LPAREN tok_decl(0,0)
12005
12006#define TOK_COMMA tok_decl(1,0)
12007
12008#define TOK_ASSIGN tok_decl(2,0)
12009#define TOK_AND_ASSIGN tok_decl(2,1)
12010#define TOK_OR_ASSIGN tok_decl(2,2)
12011#define TOK_XOR_ASSIGN tok_decl(2,3)
12012#define TOK_PLUS_ASSIGN tok_decl(2,4)
12013#define TOK_MINUS_ASSIGN tok_decl(2,5)
12014#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12015#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12016
12017#define TOK_MUL_ASSIGN tok_decl(3,0)
12018#define TOK_DIV_ASSIGN tok_decl(3,1)
12019#define TOK_REM_ASSIGN tok_decl(3,2)
12020
12021/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012022#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012023
12024/* conditional is right associativity too */
12025#define TOK_CONDITIONAL tok_decl(4,0)
12026#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12027
12028#define TOK_OR tok_decl(5,0)
12029
12030#define TOK_AND tok_decl(6,0)
12031
12032#define TOK_BOR tok_decl(7,0)
12033
12034#define TOK_BXOR tok_decl(8,0)
12035
12036#define TOK_BAND tok_decl(9,0)
12037
12038#define TOK_EQ tok_decl(10,0)
12039#define TOK_NE tok_decl(10,1)
12040
12041#define TOK_LT tok_decl(11,0)
12042#define TOK_GT tok_decl(11,1)
12043#define TOK_GE tok_decl(11,2)
12044#define TOK_LE tok_decl(11,3)
12045
12046#define TOK_LSHIFT tok_decl(12,0)
12047#define TOK_RSHIFT tok_decl(12,1)
12048
12049#define TOK_ADD tok_decl(13,0)
12050#define TOK_SUB tok_decl(13,1)
12051
12052#define TOK_MUL tok_decl(14,0)
12053#define TOK_DIV tok_decl(14,1)
12054#define TOK_REM tok_decl(14,2)
12055
12056/* exponent is right associativity */
12057#define TOK_EXPONENT tok_decl(15,1)
12058
12059/* For now unary operators. */
12060#define UNARYPREC 16
12061#define TOK_BNOT tok_decl(UNARYPREC,0)
12062#define TOK_NOT tok_decl(UNARYPREC,1)
12063
12064#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12065#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12066
12067#define PREC_PRE (UNARYPREC+2)
12068
12069#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12070#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12071
12072#define PREC_POST (UNARYPREC+3)
12073
12074#define TOK_POST_INC tok_decl(PREC_POST, 0)
12075#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12076
12077#define SPEC_PREC (UNARYPREC+4)
12078
12079#define TOK_NUM tok_decl(SPEC_PREC, 0)
12080#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12081
12082#define NUMPTR (*numstackptr)
12083
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012084static int
12085tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012086{
12087 operator prec = PREC(op);
12088
12089 convert_prec_is_assing(prec);
12090 return (prec == PREC(TOK_ASSIGN) ||
12091 prec == PREC_PRE || prec == PREC_POST);
12092}
12093
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012094static int
12095is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012096{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012097 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12098 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012099}
12100
Eric Andersen90898442003-08-06 11:20:52 +000012101typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012102 arith_t val;
12103 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012104 char contidional_second_val_initialized;
12105 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012106 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012107} v_n_t;
12108
Eric Andersen90898442003-08-06 11:20:52 +000012109typedef struct CHK_VAR_RECURSIVE_LOOPED {
12110 const char *var;
12111 struct CHK_VAR_RECURSIVE_LOOPED *next;
12112} chk_var_recursive_looped_t;
12113
12114static chk_var_recursive_looped_t *prev_chk_var_recursive;
12115
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012116static int
12117arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012118{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012119 if (t->var) {
12120 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012121
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012122 if (p) {
12123 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012124
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012125 /* recursive try as expression */
12126 chk_var_recursive_looped_t *cur;
12127 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012128
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012129 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12130 if (strcmp(cur->var, t->var) == 0) {
12131 /* expression recursion loop detected */
12132 return -5;
12133 }
12134 }
12135 /* save current lookuped var name */
12136 cur = prev_chk_var_recursive;
12137 cur_save.var = t->var;
12138 cur_save.next = cur;
12139 prev_chk_var_recursive = &cur_save;
12140
12141 t->val = arith (p, &errcode);
12142 /* restore previous ptr after recursiving */
12143 prev_chk_var_recursive = cur;
12144 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012145 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012146 /* allow undefined var as 0 */
12147 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012148 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012149 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012150}
12151
12152/* "applying" a token means performing it on the top elements on the integer
12153 * stack. For a unary operator it will only change the top element, but a
12154 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012155static int
12156arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012157{
Eric Andersen90898442003-08-06 11:20:52 +000012158 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012159 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012160 int ret_arith_lookup_val;
12161
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012162 /* There is no operator that can work without arguments */
12163 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012164 numptr_m1 = NUMPTR - 1;
12165
12166 /* check operand is var with noninteger value */
12167 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012168 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012169 return ret_arith_lookup_val;
12170
12171 rez = numptr_m1->val;
12172 if (op == TOK_UMINUS)
12173 rez *= -1;
12174 else if (op == TOK_NOT)
12175 rez = !rez;
12176 else if (op == TOK_BNOT)
12177 rez = ~rez;
12178 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12179 rez++;
12180 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12181 rez--;
12182 else if (op != TOK_UPLUS) {
12183 /* Binary operators */
12184
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012185 /* check and binary operators need two arguments */
12186 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012187
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012188 /* ... and they pop one */
12189 --NUMPTR;
12190 numptr_val = rez;
12191 if (op == TOK_CONDITIONAL) {
12192 if (! numptr_m1->contidional_second_val_initialized) {
12193 /* protect $((expr1 ? expr2)) without ": expr" */
12194 goto err;
12195 }
12196 rez = numptr_m1->contidional_second_val;
12197 } else if (numptr_m1->contidional_second_val_initialized) {
12198 /* protect $((expr1 : expr2)) without "expr ? " */
12199 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012200 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012201 numptr_m1 = NUMPTR - 1;
12202 if (op != TOK_ASSIGN) {
12203 /* check operand is var with noninteger value for not '=' */
12204 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12205 if (ret_arith_lookup_val)
12206 return ret_arith_lookup_val;
12207 }
12208 if (op == TOK_CONDITIONAL) {
12209 numptr_m1->contidional_second_val = rez;
12210 }
12211 rez = numptr_m1->val;
12212 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012213 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012214 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012215 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012216 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012217 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012218 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012219 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012220 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012221 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012222 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012223 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012224 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012225 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012226 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012227 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012228 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012229 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012230 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012231 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012232 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012233 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012234 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012235 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012236 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012237 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012238 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012239 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012240 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012241 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012242 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012243 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012244 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012245 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012246 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012247 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012248 /* protect $((expr : expr)) without "expr ? " */
12249 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012250 }
12251 numptr_m1->contidional_second_val_initialized = op;
12252 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012253 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012254 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012255 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012256 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012257 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012258 return -3; /* exponent less than 0 */
12259 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012260 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012261
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012262 if (numptr_val)
12263 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012264 c *= rez;
12265 rez = c;
12266 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012267 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012268 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012269 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012270 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012271 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012272 rez %= numptr_val;
12273 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012274 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012275 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012276
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012277 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012278 /* Hmm, 1=2 ? */
12279 goto err;
12280 }
12281 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012282#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012283 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012284#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012285 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012286#endif
Eric Andersen90898442003-08-06 11:20:52 +000012287 setvar(numptr_m1->var, buf, 0);
12288 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012289 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012290 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012291 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012292 rez++;
12293 }
12294 numptr_m1->val = rez;
12295 /* protect geting var value, is number now */
12296 numptr_m1->var = NULL;
12297 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012298 err:
12299 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012300}
12301
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012302/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012303static const char op_tokens[] = {
12304 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12305 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12306 '<','<', 0, TOK_LSHIFT,
12307 '>','>', 0, TOK_RSHIFT,
12308 '|','|', 0, TOK_OR,
12309 '&','&', 0, TOK_AND,
12310 '!','=', 0, TOK_NE,
12311 '<','=', 0, TOK_LE,
12312 '>','=', 0, TOK_GE,
12313 '=','=', 0, TOK_EQ,
12314 '|','=', 0, TOK_OR_ASSIGN,
12315 '&','=', 0, TOK_AND_ASSIGN,
12316 '*','=', 0, TOK_MUL_ASSIGN,
12317 '/','=', 0, TOK_DIV_ASSIGN,
12318 '%','=', 0, TOK_REM_ASSIGN,
12319 '+','=', 0, TOK_PLUS_ASSIGN,
12320 '-','=', 0, TOK_MINUS_ASSIGN,
12321 '-','-', 0, TOK_POST_DEC,
12322 '^','=', 0, TOK_XOR_ASSIGN,
12323 '+','+', 0, TOK_POST_INC,
12324 '*','*', 0, TOK_EXPONENT,
12325 '!', 0, TOK_NOT,
12326 '<', 0, TOK_LT,
12327 '>', 0, TOK_GT,
12328 '=', 0, TOK_ASSIGN,
12329 '|', 0, TOK_BOR,
12330 '&', 0, TOK_BAND,
12331 '*', 0, TOK_MUL,
12332 '/', 0, TOK_DIV,
12333 '%', 0, TOK_REM,
12334 '+', 0, TOK_ADD,
12335 '-', 0, TOK_SUB,
12336 '^', 0, TOK_BXOR,
12337 /* uniq */
12338 '~', 0, TOK_BNOT,
12339 ',', 0, TOK_COMMA,
12340 '?', 0, TOK_CONDITIONAL,
12341 ':', 0, TOK_CONDITIONAL_SEP,
12342 ')', 0, TOK_RPAREN,
12343 '(', 0, TOK_LPAREN,
12344 0
12345};
12346/* ptr to ")" */
12347#define endexpression &op_tokens[sizeof(op_tokens)-7]
12348
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012349static arith_t
12350arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012351{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012352 char arithval; /* Current character under analysis */
12353 operator lasttok, op;
12354 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012355
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012356 const char *p = endexpression;
12357 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012358
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012359 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012360
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012361 /* Stack of integers */
12362 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12363 * in any given correct or incorrect expression is left as an exercise to
12364 * the reader. */
12365 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12366 *numstackptr = numstack;
12367 /* Stack of operator tokens */
12368 operator *stack = alloca((datasizes) * sizeof(operator)),
12369 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012370
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012371 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12372 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012373
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012374 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012375 arithval = *expr;
12376 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012377 if (p == endexpression) {
12378 /* Null expression. */
12379 return 0;
12380 }
12381
12382 /* This is only reached after all tokens have been extracted from the
12383 * input stream. If there are still tokens on the operator stack, they
12384 * are to be applied in order. At the end, there should be a final
12385 * result on the integer stack */
12386
12387 if (expr != endexpression + 1) {
12388 /* If we haven't done so already, */
12389 /* append a closing right paren */
12390 expr = endexpression;
12391 /* and let the loop process it. */
12392 continue;
12393 }
12394 /* At this point, we're done with the expression. */
12395 if (numstackptr != numstack+1) {
12396 /* ... but if there isn't, it's bad */
12397 err:
12398 return (*perrcode = -1);
12399 }
12400 if (numstack->var) {
12401 /* expression is $((var)) only, lookup now */
12402 errcode = arith_lookup_val(numstack);
12403 }
12404 ret:
12405 *perrcode = errcode;
12406 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012407 }
12408
Eric Andersen90898442003-08-06 11:20:52 +000012409 /* Continue processing the expression. */
12410 if (arith_isspace(arithval)) {
12411 /* Skip whitespace */
12412 goto prologue;
12413 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012414 p = endofname(expr);
12415 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012416 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012417
12418 numstackptr->var = alloca(var_name_size);
12419 safe_strncpy(numstackptr->var, expr, var_name_size);
12420 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012421 num:
Eric Andersen90898442003-08-06 11:20:52 +000012422 numstackptr->contidional_second_val_initialized = 0;
12423 numstackptr++;
12424 lasttok = TOK_NUM;
12425 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012426 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012427 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012428 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012429#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012430 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012431#else
12432 numstackptr->val = strtol(expr, (char **) &expr, 0);
12433#endif
Eric Andersen90898442003-08-06 11:20:52 +000012434 goto num;
12435 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012436 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012437 const char *o;
12438
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012439 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012440 /* strange operator not found */
12441 goto err;
12442 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012443 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012444 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012446 /* found */
12447 expr = o - 1;
12448 break;
12449 }
12450 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012451 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012452 p++;
12453 /* skip zero delim */
12454 p++;
12455 }
12456 op = p[1];
12457
12458 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012459 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12460 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012461
12462 /* Plus and minus are binary (not unary) _only_ if the last
12463 * token was as number, or a right paren (which pretends to be
12464 * a number, since it evaluates to one). Think about it.
12465 * It makes sense. */
12466 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012467 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012468 case TOK_ADD:
12469 op = TOK_UPLUS;
12470 break;
12471 case TOK_SUB:
12472 op = TOK_UMINUS;
12473 break;
12474 case TOK_POST_INC:
12475 op = TOK_PRE_INC;
12476 break;
12477 case TOK_POST_DEC:
12478 op = TOK_PRE_DEC;
12479 break;
Eric Andersen90898442003-08-06 11:20:52 +000012480 }
12481 }
12482 /* We don't want a unary operator to cause recursive descent on the
12483 * stack, because there can be many in a row and it could cause an
12484 * operator to be evaluated before its argument is pushed onto the
12485 * integer stack. */
12486 /* But for binary operators, "apply" everything on the operator
12487 * stack until we find an operator with a lesser priority than the
12488 * one we have just extracted. */
12489 /* Left paren is given the lowest priority so it will never be
12490 * "applied" in this way.
12491 * if associativity is right and priority eq, applied also skip
12492 */
12493 prec = PREC(op);
12494 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12495 /* not left paren or unary */
12496 if (lasttok != TOK_NUM) {
12497 /* binary op must be preceded by a num */
12498 goto err;
12499 }
12500 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012501 if (op == TOK_RPAREN) {
12502 /* The algorithm employed here is simple: while we don't
12503 * hit an open paren nor the bottom of the stack, pop
12504 * tokens and apply them */
12505 if (stackptr[-1] == TOK_LPAREN) {
12506 --stackptr;
12507 /* Any operator directly after a */
12508 lasttok = TOK_NUM;
12509 /* close paren should consider itself binary */
12510 goto prologue;
12511 }
12512 } else {
12513 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012514
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012515 convert_prec_is_assing(prec);
12516 convert_prec_is_assing(prev_prec);
12517 if (prev_prec < prec)
12518 break;
12519 /* check right assoc */
12520 if (prev_prec == prec && is_right_associativity(prec))
12521 break;
12522 }
12523 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12524 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012525 }
12526 if (op == TOK_RPAREN) {
12527 goto err;
12528 }
12529 }
12530
12531 /* Push this operator to the stack and remember it. */
12532 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012533 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012534 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012535 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012536}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012537#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012538
12539
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012540/* ============ main() and helpers */
12541
12542/*
12543 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012544 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012545static void exitshell(void) ATTRIBUTE_NORETURN;
12546static void
12547exitshell(void)
12548{
12549 struct jmploc loc;
12550 char *p;
12551 int status;
12552
12553 status = exitstatus;
12554 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12555 if (setjmp(loc.loc)) {
12556 if (exception == EXEXIT)
12557/* dash bug: it just does _exit(exitstatus) here
12558 * but we have to do setjobctl(0) first!
12559 * (bug is still not fixed in dash-0.5.3 - if you run dash
12560 * under Midnight Commander, on exit from dash MC is backgrounded) */
12561 status = exitstatus;
12562 goto out;
12563 }
12564 exception_handler = &loc;
12565 p = trap[0];
12566 if (p) {
12567 trap[0] = NULL;
12568 evalstring(p, 0);
12569 }
12570 flush_stdout_stderr();
12571 out:
12572 setjobctl(0);
12573 _exit(status);
12574 /* NOTREACHED */
12575}
12576
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012577static void
12578init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012579{
12580 /* from input.c: */
12581 basepf.nextc = basepf.buf = basebuf;
12582
12583 /* from trap.c: */
12584 signal(SIGCHLD, SIG_DFL);
12585
12586 /* from var.c: */
12587 {
12588 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012589 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012590 const char *p;
12591 struct stat st1, st2;
12592
12593 initvar();
12594 for (envp = environ; envp && *envp; envp++) {
12595 if (strchr(*envp, '=')) {
12596 setvareq(*envp, VEXPORT|VTEXTFIXED);
12597 }
12598 }
12599
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012600 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012601 setvar("PPID", ppid, 0);
12602
12603 p = lookupvar("PWD");
12604 if (p)
12605 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12606 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12607 p = '\0';
12608 setpwd(p, 0);
12609 }
12610}
12611
12612/*
12613 * Process the shell command line arguments.
12614 */
12615static void
12616procargs(int argc, char **argv)
12617{
12618 int i;
12619 const char *xminusc;
12620 char **xargv;
12621
12622 xargv = argv;
12623 arg0 = xargv[0];
12624 if (argc > 0)
12625 xargv++;
12626 for (i = 0; i < NOPTS; i++)
12627 optlist[i] = 2;
12628 argptr = xargv;
12629 options(1);
12630 xargv = argptr;
12631 xminusc = minusc;
12632 if (*xargv == NULL) {
12633 if (xminusc)
12634 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12635 sflag = 1;
12636 }
12637 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12638 iflag = 1;
12639 if (mflag == 2)
12640 mflag = iflag;
12641 for (i = 0; i < NOPTS; i++)
12642 if (optlist[i] == 2)
12643 optlist[i] = 0;
12644#if DEBUG == 2
12645 debug = 1;
12646#endif
12647 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12648 if (xminusc) {
12649 minusc = *xargv++;
12650 if (*xargv)
12651 goto setarg0;
12652 } else if (!sflag) {
12653 setinputfile(*xargv, 0);
12654 setarg0:
12655 arg0 = *xargv++;
12656 commandname = arg0;
12657 }
12658
12659 shellparam.p = xargv;
12660#if ENABLE_ASH_GETOPTS
12661 shellparam.optind = 1;
12662 shellparam.optoff = -1;
12663#endif
12664 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12665 while (*xargv) {
12666 shellparam.nparam++;
12667 xargv++;
12668 }
12669 optschanged();
12670}
12671
12672/*
12673 * Read /etc/profile or .profile.
12674 */
12675static void
12676read_profile(const char *name)
12677{
12678 int skip;
12679
12680 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12681 return;
12682 skip = cmdloop(0);
12683 popfile();
12684 if (skip)
12685 exitshell();
12686}
12687
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012688/*
12689 * This routine is called when an error or an interrupt occurs in an
12690 * interactive shell and control is returned to the main command loop.
12691 */
12692static void
12693reset(void)
12694{
12695 /* from eval.c: */
12696 evalskip = 0;
12697 loopnest = 0;
12698 /* from input.c: */
12699 parselleft = parsenleft = 0; /* clear input buffer */
12700 popallfiles();
12701 /* from parser.c: */
12702 tokpushback = 0;
12703 checkkwd = 0;
12704 /* from redir.c: */
12705 clearredir(0);
12706}
12707
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012708#if PROFILE
12709static short profile_buf[16384];
12710extern int etext();
12711#endif
12712
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012713/*
12714 * Main routine. We initialize things, parse the arguments, execute
12715 * profiles if we're a login shell, and then call cmdloop to execute
12716 * commands. The setjmp call sets up the location to jump to when an
12717 * exception occurs. When an exception occurs the variable "state"
12718 * is used to figure out how far we had gotten.
12719 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012720int ash_main(int argc, char **argv);
12721int ash_main(int argc, char **argv)
12722{
12723 char *shinit;
12724 volatile int state;
12725 struct jmploc jmploc;
12726 struct stackmark smark;
12727
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012728#if PROFILE
12729 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12730#endif
12731
12732#if ENABLE_FEATURE_EDITING
12733 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12734#endif
12735 state = 0;
12736 if (setjmp(jmploc.loc)) {
12737 int e;
12738 int s;
12739
12740 reset();
12741
12742 e = exception;
12743 if (e == EXERROR)
12744 exitstatus = 2;
12745 s = state;
12746 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12747 exitshell();
12748
12749 if (e == EXINT) {
12750 outcslow('\n', stderr);
12751 }
12752 popstackmark(&smark);
12753 FORCE_INT_ON; /* enable interrupts */
12754 if (s == 1)
12755 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012756 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012757 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012758 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012759 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012760 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012761 }
12762 exception_handler = &jmploc;
12763#if DEBUG
12764 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012765 trace_puts("Shell args: ");
12766 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012767#endif
12768 rootpid = getpid();
12769
12770#if ENABLE_ASH_RANDOM_SUPPORT
12771 rseed = rootpid + time(NULL);
12772#endif
12773 init();
12774 setstackmark(&smark);
12775 procargs(argc, argv);
12776#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12777 if (iflag) {
12778 const char *hp = lookupvar("HISTFILE");
12779
12780 if (hp == NULL) {
12781 hp = lookupvar("HOME");
12782 if (hp != NULL) {
12783 char *defhp = concat_path_file(hp, ".ash_history");
12784 setvar("HISTFILE", defhp, 0);
12785 free(defhp);
12786 }
12787 }
12788 }
12789#endif
12790 if (argv[0] && argv[0][0] == '-')
12791 isloginsh = 1;
12792 if (isloginsh) {
12793 state = 1;
12794 read_profile("/etc/profile");
12795 state1:
12796 state = 2;
12797 read_profile(".profile");
12798 }
12799 state2:
12800 state = 3;
12801 if (
12802#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012803 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012804#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012805 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012806 ) {
12807 shinit = lookupvar("ENV");
12808 if (shinit != NULL && *shinit != '\0') {
12809 read_profile(shinit);
12810 }
12811 }
12812 state3:
12813 state = 4;
12814 if (minusc)
12815 evalstring(minusc, 0);
12816
12817 if (sflag || minusc == NULL) {
12818#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12819 if ( iflag ) {
12820 const char *hp = lookupvar("HISTFILE");
12821
12822 if (hp != NULL)
12823 line_input_state->hist_file = hp;
12824 }
12825#endif
12826 state4: /* XXX ??? - why isn't this before the "if" statement */
12827 cmdloop(1);
12828 }
12829#if PROFILE
12830 monitor(0);
12831#endif
12832#ifdef GPROF
12833 {
12834 extern void _mcleanup(void);
12835 _mcleanup();
12836 }
12837#endif
12838 exitshell();
12839 /* NOTREACHED */
12840}
12841
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012842#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012843const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012844int main(int argc, char **argv)
12845{
12846 return ash_main(argc, argv);
12847}
12848#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012849
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012850
Eric Andersendf82f612001-06-28 07:46:40 +000012851/*-
12852 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012853 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012854 *
12855 * This code is derived from software contributed to Berkeley by
12856 * Kenneth Almquist.
12857 *
12858 * Redistribution and use in source and binary forms, with or without
12859 * modification, are permitted provided that the following conditions
12860 * are met:
12861 * 1. Redistributions of source code must retain the above copyright
12862 * notice, this list of conditions and the following disclaimer.
12863 * 2. Redistributions in binary form must reproduce the above copyright
12864 * notice, this list of conditions and the following disclaimer in the
12865 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012866 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012867 * may be used to endorse or promote products derived from this software
12868 * without specific prior written permission.
12869 *
12870 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12871 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12872 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12873 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12874 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12875 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12876 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12877 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12878 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12879 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12880 * SUCH DAMAGE.
12881 */