blob: 94333360b96cd6bbf6bd9f203fca7e66f009c88f [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
56#include "busybox.h"
57#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
63
Denis Vlasenkob012b102007-02-19 22:43:01 +000064#if defined(__uClinux__)
65#error "Do not even bother, ash will not run on uClinux"
66#endif
67
Denis Vlasenkob012b102007-02-19 22:43:01 +000068
69/* ============ Shell options */
70
71static const char *const optletters_optnames[] = {
72 "e" "errexit",
73 "f" "noglob",
74 "I" "ignoreeof",
75 "i" "interactive",
76 "m" "monitor",
77 "n" "noexec",
78 "s" "stdin",
79 "x" "xtrace",
80 "v" "verbose",
81 "C" "noclobber",
82 "a" "allexport",
83 "b" "notify",
84 "u" "nounset",
85 "\0" "vi",
86#if DEBUG
87 "\0" "nolog",
88 "\0" "debug",
89#endif
90};
91
92#define optletters(n) optletters_optnames[(n)][0]
93#define optnames(n) (&optletters_optnames[(n)][1])
94
95#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
96
97static char optlist[NOPTS];
98
99#define eflag optlist[0]
100#define fflag optlist[1]
101#define Iflag optlist[2]
102#define iflag optlist[3]
103#define mflag optlist[4]
104#define nflag optlist[5]
105#define sflag optlist[6]
106#define xflag optlist[7]
107#define vflag optlist[8]
108#define Cflag optlist[9]
109#define aflag optlist[10]
110#define bflag optlist[11]
111#define uflag optlist[12]
112#define viflag optlist[13]
113#if DEBUG
114#define nolog optlist[14]
115#define debug optlist[15]
116#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000117
118
Denis Vlasenkob012b102007-02-19 22:43:01 +0000119/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000120
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000121#ifdef __GLIBC__
122/* glibc sucks */
123static int *dash_errno;
124#undef errno
125#define errno (*dash_errno)
126#endif
127
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000128static char nullstr[1]; /* zero length string */
129static const char homestr[] = "HOME";
130static const char snlfmt[] = "%s\n";
131static const char illnum[] = "Illegal number: %s";
132
Denis Vlasenkoa624c112007-02-19 22:45:43 +0000133static int isloginsh;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000134/* pid of main shell */
135static int rootpid;
136/* shell level: 0 for the main shell, 1 for its children, and so on */
137static int shlvl;
138#define rootshell (!shlvl)
139/* trap handler commands */
140static char *trap[NSIG];
141/* current value of signal */
142static char sigmode[NSIG - 1];
143/* indicates specified signal received */
144static char gotsig[NSIG - 1];
145static char *arg0; /* value of $0 */
Eric Andersenc470f442003-07-28 09:56:35 +0000146
147
Denis Vlasenkob012b102007-02-19 22:43:01 +0000148/* ============ Interrupts / exceptions
149 *
Eric Andersenc470f442003-07-28 09:56:35 +0000150 * We enclose jmp_buf in a structure so that we can declare pointers to
151 * jump locations. The global variable handler contains the location to
152 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000153 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000154 * exception handlers, the user should save the value of handler on entry
155 * to an inner scope, set handler to point to a jmploc structure for the
156 * inner scope, and restore handler on exit from the scope.
157 */
Eric Andersenc470f442003-07-28 09:56:35 +0000158struct jmploc {
159 jmp_buf loc;
160};
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000161static struct jmploc *exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +0000162static int exception;
Eric Andersenc470f442003-07-28 09:56:35 +0000163/* exceptions */
164#define EXINT 0 /* SIGINT received */
165#define EXERROR 1 /* a generic error */
166#define EXSHELLPROC 2 /* execute a shell procedure */
167#define EXEXEC 3 /* command execution failed */
168#define EXEXIT 4 /* exit the shell */
169#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000170static volatile int suppressint;
171static volatile sig_atomic_t intpending;
Eric Andersenc470f442003-07-28 09:56:35 +0000172/* do we generate EXSIG events */
173static int exsig;
174/* last pending signal */
175static volatile sig_atomic_t pendingsigs;
Eric Andersen2870d962001-07-02 17:27:21 +0000176
177/*
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000178 * Sigmode records the current value of the signal handlers for the various
179 * modes. A value of zero means that the current handler is not known.
180 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
181 */
182
183#define S_DFL 1 /* default signal handling (SIG_DFL) */
184#define S_CATCH 2 /* signal is caught */
185#define S_IGN 3 /* signal is ignored (SIG_IGN) */
186#define S_HARD_IGN 4 /* signal is ignored permenantly */
187#define S_RESET 5 /* temporary - to reset a hard ignored sig */
188
189/*
Eric Andersen2870d962001-07-02 17:27:21 +0000190 * These macros allow the user to suspend the handling of interrupt signals
191 * over a period of time. This is similar to SIGHOLD to or sigblock, but
192 * much more efficient and portable. (But hacking the kernel is so much
193 * more fun than worrying about efficiency and portability. :-))
194 */
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000195#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
Denis Vlasenkob012b102007-02-19 22:43:01 +0000196#define INT_OFF \
Eric Andersenc470f442003-07-28 09:56:35 +0000197 ({ \
198 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000199 xbarrier(); \
Eric Andersenc470f442003-07-28 09:56:35 +0000200 0; \
201 })
Denis Vlasenkob012b102007-02-19 22:43:01 +0000202
203/*
204 * Called to raise an exception. Since C doesn't include exceptions, we
205 * just do a longjmp to the exception handler. The type of exception is
206 * stored in the global variable "exception".
207 */
208static void raise_exception(int) ATTRIBUTE_NORETURN;
209static void
210raise_exception(int e)
211{
212#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000213 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000214 abort();
215#endif
216 INT_OFF;
217 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000218 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000219}
220
221/*
222 * Called from trap.c when a SIGINT is received. (If the user specifies
223 * that SIGINT is to be trapped or ignored using the trap builtin, then
224 * this routine is not called.) Suppressint is nonzero when interrupts
225 * are held using the INT_OFF macro. (The test for iflag is just
226 * defensive programming.)
227 */
228static void raise_interrupt(void) ATTRIBUTE_NORETURN;
229static void
230raise_interrupt(void)
231{
232 int i;
233
234 intpending = 0;
235 i = EXSIG;
236 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
237 if (!(rootshell && iflag)) {
238 signal(SIGINT, SIG_DFL);
239 raise(SIGINT);
240 }
241 i = EXINT;
242 }
243 raise_exception(i);
244 /* NOTREACHED */
245}
246
247#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000248static void
249int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000250{
251 if (--suppressint == 0 && intpending) {
252 raise_interrupt();
253 }
254}
255#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000256static void
257force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000258{
259 suppressint = 0;
260 if (intpending)
261 raise_interrupt();
262}
263#define FORCE_INT_ON force_int_on()
264#else
265#define INT_ON \
266 ({ \
267 xbarrier(); \
268 if (--suppressint == 0 && intpending) raise_interrupt(); \
269 0; \
270 })
271#define FORCE_INT_ON \
272 ({ \
273 xbarrier(); \
274 suppressint = 0; \
275 if (intpending) raise_interrupt(); \
276 0; \
277 })
278#endif /* ASH_OPTIMIZE_FOR_SIZE */
279
280#define SAVE_INT(v) ((v) = suppressint)
281
282#define RESTORE_INT(v) \
Eric Andersenc470f442003-07-28 09:56:35 +0000283 ({ \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000284 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000285 suppressint = (v); \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000286 if (suppressint == 0 && intpending) raise_interrupt(); \
Eric Andersenc470f442003-07-28 09:56:35 +0000287 0; \
288 })
Denis Vlasenkob012b102007-02-19 22:43:01 +0000289
290#define EXSIGON \
Eric Andersenc470f442003-07-28 09:56:35 +0000291 ({ \
292 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000293 xbarrier(); \
Eric Andersenc470f442003-07-28 09:56:35 +0000294 if (pendingsigs) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000295 raise_exception(EXSIG); \
Eric Andersenc470f442003-07-28 09:56:35 +0000296 0; \
297 })
298/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000299
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000300/*
301 * Ignore a signal. Only one usage site - in forkchild()
302 */
303static void
304ignoresig(int signo)
305{
306 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
307 signal(signo, SIG_IGN);
308 }
309 sigmode[signo - 1] = S_HARD_IGN;
310}
311
312/*
313 * Signal handler. Only one usage site - in setsignal()
314 */
315static void
316onsig(int signo)
317{
318 gotsig[signo - 1] = 1;
319 pendingsigs = signo;
320
321 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
322 if (!suppressint)
323 raise_interrupt();
324 intpending = 1;
325 }
326}
327
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000328
Denis Vlasenkob012b102007-02-19 22:43:01 +0000329/* ============ stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000330
Eric Andersenc470f442003-07-28 09:56:35 +0000331static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000332outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000333{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000334 INT_OFF;
335 fputs(p, file);
336 INT_ON;
337}
338
339static void
340flush_stdout_stderr(void)
341{
342 INT_OFF;
343 fflush(stdout);
344 fflush(stderr);
345 INT_ON;
346}
347
348static void
349flush_stderr(void)
350{
351 INT_OFF;
352 fflush(stderr);
353 INT_ON;
354}
355
356static void
357outcslow(int c, FILE *dest)
358{
359 INT_OFF;
360 putc(c, dest);
361 fflush(dest);
362 INT_ON;
363}
364
365static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
366static int
367out1fmt(const char *fmt, ...)
368{
369 va_list ap;
370 int r;
371
372 INT_OFF;
373 va_start(ap, fmt);
374 r = vprintf(fmt, ap);
375 va_end(ap);
376 INT_ON;
377 return r;
378}
379
380static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
381static int
382fmtstr(char *outbuf, size_t length, const char *fmt, ...)
383{
384 va_list ap;
385 int ret;
386
387 va_start(ap, fmt);
388 INT_OFF;
389 ret = vsnprintf(outbuf, length, fmt, ap);
390 va_end(ap);
391 INT_ON;
392 return ret;
393}
394
395static void
396out1str(const char *p)
397{
398 outstr(p, stdout);
399}
400
401static void
402out2str(const char *p)
403{
404 outstr(p, stderr);
405 flush_stderr();
406}
407
408
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000409/* ============ Parsing structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000410
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000411#define NCMD 0
412#define NPIPE 1
413#define NREDIR 2
414#define NBACKGND 3
415#define NSUBSHELL 4
416#define NAND 5
417#define NOR 6
418#define NSEMI 7
419#define NIF 8
420#define NWHILE 9
421#define NUNTIL 10
422#define NFOR 11
423#define NCASE 12
424#define NCLIST 13
425#define NDEFUN 14
426#define NARG 15
427#define NTO 16
428#define NCLOBBER 17
429#define NFROM 18
430#define NFROMTO 19
431#define NAPPEND 20
432#define NTOFD 21
433#define NFROMFD 22
434#define NHERE 23
435#define NXHERE 24
436#define NNOT 25
437
438union node;
439
440struct ncmd {
441 int type;
442 union node *assign;
443 union node *args;
444 union node *redirect;
445};
446
447struct npipe {
448 int type;
449 int backgnd;
450 struct nodelist *cmdlist;
451};
452
453struct nredir {
454 int type;
455 union node *n;
456 union node *redirect;
457};
458
459struct nbinary {
460 int type;
461 union node *ch1;
462 union node *ch2;
463};
464
465struct nif {
466 int type;
467 union node *test;
468 union node *ifpart;
469 union node *elsepart;
470};
471
472struct nfor {
473 int type;
474 union node *args;
475 union node *body;
476 char *var;
477};
478
479struct ncase {
480 int type;
481 union node *expr;
482 union node *cases;
483};
484
485struct nclist {
486 int type;
487 union node *next;
488 union node *pattern;
489 union node *body;
490};
491
492struct narg {
493 int type;
494 union node *next;
495 char *text;
496 struct nodelist *backquote;
497};
498
499struct nfile {
500 int type;
501 union node *next;
502 int fd;
503 union node *fname;
504 char *expfname;
505};
506
507struct ndup {
508 int type;
509 union node *next;
510 int fd;
511 int dupfd;
512 union node *vname;
513};
514
515struct nhere {
516 int type;
517 union node *next;
518 int fd;
519 union node *doc;
520};
521
522struct nnot {
523 int type;
524 union node *com;
525};
526
527union node {
528 int type;
529 struct ncmd ncmd;
530 struct npipe npipe;
531 struct nredir nredir;
532 struct nbinary nbinary;
533 struct nif nif;
534 struct nfor nfor;
535 struct ncase ncase;
536 struct nclist nclist;
537 struct narg narg;
538 struct nfile nfile;
539 struct ndup ndup;
540 struct nhere nhere;
541 struct nnot nnot;
542};
543
544struct nodelist {
545 struct nodelist *next;
546 union node *n;
547};
548
549struct funcnode {
550 int count;
551 union node n;
552};
553
554
555/* ============ Debugging output */
556
557#if DEBUG
558
559static FILE *tracefile;
560
561static void
562trace_printf(const char *fmt, ...)
563{
564 va_list va;
565
566 if (debug != 1)
567 return;
568 va_start(va, fmt);
569 vfprintf(tracefile, fmt, va);
570 va_end(va);
571}
572
573static void
574trace_vprintf(const char *fmt, va_list va)
575{
576 if (debug != 1)
577 return;
578 vfprintf(tracefile, fmt, va);
579}
580
581static void
582trace_puts(const char *s)
583{
584 if (debug != 1)
585 return;
586 fputs(s, tracefile);
587}
588
589static void
590trace_puts_quoted(char *s)
591{
592 char *p;
593 char c;
594
595 if (debug != 1)
596 return;
597 putc('"', tracefile);
598 for (p = s; *p; p++) {
599 switch (*p) {
600 case '\n': c = 'n'; goto backslash;
601 case '\t': c = 't'; goto backslash;
602 case '\r': c = 'r'; goto backslash;
603 case '"': c = '"'; goto backslash;
604 case '\\': c = '\\'; goto backslash;
605 case CTLESC: c = 'e'; goto backslash;
606 case CTLVAR: c = 'v'; goto backslash;
607 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
608 case CTLBACKQ: c = 'q'; goto backslash;
609 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
610 backslash:
611 putc('\\', tracefile);
612 putc(c, tracefile);
613 break;
614 default:
615 if (*p >= ' ' && *p <= '~')
616 putc(*p, tracefile);
617 else {
618 putc('\\', tracefile);
619 putc(*p >> 6 & 03, tracefile);
620 putc(*p >> 3 & 07, tracefile);
621 putc(*p & 07, tracefile);
622 }
623 break;
624 }
625 }
626 putc('"', tracefile);
627}
628
629static void
630trace_puts_args(char **ap)
631{
632 if (debug != 1)
633 return;
634 if (!*ap)
635 return;
636 while (1) {
637 trace_puts_quoted(*ap);
638 if (!*++ap) {
639 putc('\n', tracefile);
640 break;
641 }
642 putc(' ', tracefile);
643 }
644}
645
646static void
647opentrace(void)
648{
649 char s[100];
650#ifdef O_APPEND
651 int flags;
652#endif
653
654 if (debug != 1) {
655 if (tracefile)
656 fflush(tracefile);
657 /* leave open because libedit might be using it */
658 return;
659 }
660 strcpy(s, "./trace");
661 if (tracefile) {
662 if (!freopen(s, "a", tracefile)) {
663 fprintf(stderr, "Can't re-open %s\n", s);
664 debug = 0;
665 return;
666 }
667 } else {
668 tracefile = fopen(s, "a");
669 if (tracefile == NULL) {
670 fprintf(stderr, "Can't open %s\n", s);
671 debug = 0;
672 return;
673 }
674 }
675#ifdef O_APPEND
676 flags = fcntl(fileno(tracefile), F_GETFL, 0);
677 if (flags >= 0)
678 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
679#endif
680 setlinebuf(tracefile);
681 fputs("\nTracing started.\n", tracefile);
682}
683
684static void
685indent(int amount, char *pfx, FILE *fp)
686{
687 int i;
688
689 for (i = 0; i < amount; i++) {
690 if (pfx && i == amount - 1)
691 fputs(pfx, fp);
692 putc('\t', fp);
693 }
694}
695
696/* little circular references here... */
697static void shtree(union node *n, int ind, char *pfx, FILE *fp);
698
699static void
700sharg(union node *arg, FILE *fp)
701{
702 char *p;
703 struct nodelist *bqlist;
704 int subtype;
705
706 if (arg->type != NARG) {
707 out1fmt("<node type %d>\n", arg->type);
708 abort();
709 }
710 bqlist = arg->narg.backquote;
711 for (p = arg->narg.text; *p; p++) {
712 switch (*p) {
713 case CTLESC:
714 putc(*++p, fp);
715 break;
716 case CTLVAR:
717 putc('$', fp);
718 putc('{', fp);
719 subtype = *++p;
720 if (subtype == VSLENGTH)
721 putc('#', fp);
722
723 while (*p != '=')
724 putc(*p++, fp);
725
726 if (subtype & VSNUL)
727 putc(':', fp);
728
729 switch (subtype & VSTYPE) {
730 case VSNORMAL:
731 putc('}', fp);
732 break;
733 case VSMINUS:
734 putc('-', fp);
735 break;
736 case VSPLUS:
737 putc('+', fp);
738 break;
739 case VSQUESTION:
740 putc('?', fp);
741 break;
742 case VSASSIGN:
743 putc('=', fp);
744 break;
745 case VSTRIMLEFT:
746 putc('#', fp);
747 break;
748 case VSTRIMLEFTMAX:
749 putc('#', fp);
750 putc('#', fp);
751 break;
752 case VSTRIMRIGHT:
753 putc('%', fp);
754 break;
755 case VSTRIMRIGHTMAX:
756 putc('%', fp);
757 putc('%', fp);
758 break;
759 case VSLENGTH:
760 break;
761 default:
762 out1fmt("<subtype %d>", subtype);
763 }
764 break;
765 case CTLENDVAR:
766 putc('}', fp);
767 break;
768 case CTLBACKQ:
769 case CTLBACKQ|CTLQUOTE:
770 putc('$', fp);
771 putc('(', fp);
772 shtree(bqlist->n, -1, NULL, fp);
773 putc(')', fp);
774 break;
775 default:
776 putc(*p, fp);
777 break;
778 }
779 }
780}
781
782static void
783shcmd(union node *cmd, FILE *fp)
784{
785 union node *np;
786 int first;
787 const char *s;
788 int dftfd;
789
790 first = 1;
791 for (np = cmd->ncmd.args; np; np = np->narg.next) {
792 if (! first)
793 putchar(' ');
794 sharg(np, fp);
795 first = 0;
796 }
797 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
798 if (! first)
799 putchar(' ');
800 switch (np->nfile.type) {
801 case NTO: s = ">"; dftfd = 1; break;
802 case NCLOBBER: s = ">|"; dftfd = 1; break;
803 case NAPPEND: s = ">>"; dftfd = 1; break;
804 case NTOFD: s = ">&"; dftfd = 1; break;
805 case NFROM: s = "<"; dftfd = 0; break;
806 case NFROMFD: s = "<&"; dftfd = 0; break;
807 case NFROMTO: s = "<>"; dftfd = 0; break;
808 default: s = "*error*"; dftfd = 0; break;
809 }
810 if (np->nfile.fd != dftfd)
811 fprintf(fp, "%d", np->nfile.fd);
812 fputs(s, fp);
813 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
814 fprintf(fp, "%d", np->ndup.dupfd);
815 } else {
816 sharg(np->nfile.fname, fp);
817 }
818 first = 0;
819 }
820}
821
822static void
823shtree(union node *n, int ind, char *pfx, FILE *fp)
824{
825 struct nodelist *lp;
826 const char *s;
827
828 if (n == NULL)
829 return;
830
831 indent(ind, pfx, fp);
832 switch (n->type) {
833 case NSEMI:
834 s = "; ";
835 goto binop;
836 case NAND:
837 s = " && ";
838 goto binop;
839 case NOR:
840 s = " || ";
841 binop:
842 shtree(n->nbinary.ch1, ind, NULL, fp);
843 /* if (ind < 0) */
844 fputs(s, fp);
845 shtree(n->nbinary.ch2, ind, NULL, fp);
846 break;
847 case NCMD:
848 shcmd(n, fp);
849 if (ind >= 0)
850 putc('\n', fp);
851 break;
852 case NPIPE:
853 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
854 shcmd(lp->n, fp);
855 if (lp->next)
856 fputs(" | ", fp);
857 }
858 if (n->npipe.backgnd)
859 fputs(" &", fp);
860 if (ind >= 0)
861 putc('\n', fp);
862 break;
863 default:
864 fprintf(fp, "<node type %d>", n->type);
865 if (ind >= 0)
866 putc('\n', fp);
867 break;
868 }
869}
870
871static void
872showtree(union node *n)
873{
874 trace_puts("showtree called\n");
875 shtree(n, 1, NULL, stdout);
876}
877
878#define TRACE(param) trace_printf param
879#define TRACEV(param) trace_vprintf param
880
881#else
882
883#define TRACE(param)
884#define TRACEV(param)
885
886#endif /* DEBUG */
887
888
Denis Vlasenkob012b102007-02-19 22:43:01 +0000889/* ============ Parser data
890 *
891 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
892 */
893
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000894struct strlist {
895 struct strlist *next;
896 char *text;
897};
898
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000899#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000900struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000901#endif
902
Denis Vlasenkob012b102007-02-19 22:43:01 +0000903struct strpush {
904 struct strpush *prev; /* preceding string on stack */
905 char *prevstring;
906 int prevnleft;
907#if ENABLE_ASH_ALIAS
908 struct alias *ap; /* if push was associated with an alias */
909#endif
910 char *string; /* remember the string since it may change */
911};
912
913struct parsefile {
914 struct parsefile *prev; /* preceding file on stack */
915 int linno; /* current line */
916 int fd; /* file descriptor (or -1 if string) */
917 int nleft; /* number of chars left in this line */
918 int lleft; /* number of chars left in this buffer */
919 char *nextc; /* next char in buffer */
920 char *buf; /* input buffer */
921 struct strpush *strpush; /* for pushing strings at this level */
922 struct strpush basestrpush; /* so pushing one is fast */
923};
924
925static struct parsefile basepf; /* top level input file */
926static struct parsefile *parsefile = &basepf; /* current input file */
927static int startlinno; /* line # where last token started */
928static char *commandname; /* currently executing command */
929static struct strlist *cmdenviron; /* environment for builtin command */
930static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000931
932
933/* ============ Message printing */
934
935static void
936ash_vmsg(const char *msg, va_list ap)
937{
938 fprintf(stderr, "%s: ", arg0);
939 if (commandname) {
940 const char *fmt = (!iflag || parsefile->fd) ?
941 "%s: %d: " : "%s: ";
942 fprintf(stderr, fmt, commandname, startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +0000943 }
Denis Vlasenkob012b102007-02-19 22:43:01 +0000944 vfprintf(stderr, msg, ap);
945 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +0000946}
Denis Vlasenkob012b102007-02-19 22:43:01 +0000947
948/*
949 * Exverror is called to raise the error exception. If the second argument
950 * is not NULL then error prints an error message using printf style
951 * formatting. It then raises the error exception.
952 */
953static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
954static void
955ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +0000956{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000957#if DEBUG
958 if (msg) {
959 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
960 TRACEV((msg, ap));
961 TRACE(("\") pid=%d\n", getpid()));
962 } else
963 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
964 if (msg)
965#endif
966 ash_vmsg(msg, ap);
967
968 flush_stdout_stderr();
969 raise_exception(cond);
970 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +0000971}
Denis Vlasenkob012b102007-02-19 22:43:01 +0000972
973static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
974static void
975ash_msg_and_raise_error(const char *msg, ...)
976{
977 va_list ap;
978
979 va_start(ap, msg);
980 ash_vmsg_and_raise(EXERROR, msg, ap);
981 /* NOTREACHED */
982 va_end(ap);
983}
984
985static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
986static void
987ash_msg_and_raise(int cond, const char *msg, ...)
988{
989 va_list ap;
990
991 va_start(ap, msg);
992 ash_vmsg_and_raise(cond, msg, ap);
993 /* NOTREACHED */
994 va_end(ap);
995}
996
997/*
998 * error/warning routines for external builtins
999 */
1000static void
1001ash_msg(const char *fmt, ...)
1002{
1003 va_list ap;
1004
1005 va_start(ap, fmt);
1006 ash_vmsg(fmt, ap);
1007 va_end(ap);
1008}
1009
1010/*
1011 * Return a string describing an error. The returned string may be a
1012 * pointer to a static buffer that will be overwritten on the next call.
1013 * Action describes the operation that got the error.
1014 */
1015static const char *
1016errmsg(int e, const char *em)
1017{
1018 if (e == ENOENT || e == ENOTDIR) {
1019 return em;
1020 }
1021 return strerror(e);
1022}
1023
1024
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001025/* ============ Memory allocation */
1026
1027/*
1028 * It appears that grabstackstr() will barf with such alignments
1029 * because stalloc() will return a string allocated in a new stackblock.
1030 */
1031#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1032enum {
1033 /* Most machines require the value returned from malloc to be aligned
1034 * in some way. The following macro will get this right
1035 * on many machines. */
1036 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1037 /* Minimum size of a block */
1038 MINSIZE = SHELL_ALIGN(504),
1039};
1040
1041struct stack_block {
1042 struct stack_block *prev;
1043 char space[MINSIZE];
1044};
1045
1046struct stackmark {
1047 struct stack_block *stackp;
1048 char *stacknxt;
1049 size_t stacknleft;
1050 struct stackmark *marknext;
1051};
1052
1053static struct stack_block stackbase;
1054static struct stack_block *stackp = &stackbase;
1055static struct stackmark *markp;
1056static char *stacknxt = stackbase.space;
1057static size_t stacknleft = MINSIZE;
1058static char *sstrend = stackbase.space + MINSIZE;
1059static int herefd = -1;
1060
1061#define stackblock() ((void *)stacknxt)
1062#define stackblocksize() stacknleft
1063
1064static void *
1065ckrealloc(void * p, size_t nbytes)
1066{
1067 p = realloc(p, nbytes);
1068 if (!p)
1069 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1070 return p;
1071}
1072
1073static void *
1074ckmalloc(size_t nbytes)
1075{
1076 return ckrealloc(NULL, nbytes);
1077}
1078
1079/*
1080 * Make a copy of a string in safe storage.
1081 */
1082static char *
1083ckstrdup(const char *s)
1084{
1085 char *p = strdup(s);
1086 if (!p)
1087 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1088 return p;
1089}
1090
1091/*
1092 * Parse trees for commands are allocated in lifo order, so we use a stack
1093 * to make this more efficient, and also to avoid all sorts of exception
1094 * handling code to handle interrupts in the middle of a parse.
1095 *
1096 * The size 504 was chosen because the Ultrix malloc handles that size
1097 * well.
1098 */
1099static void *
1100stalloc(size_t nbytes)
1101{
1102 char *p;
1103 size_t aligned;
1104
1105 aligned = SHELL_ALIGN(nbytes);
1106 if (aligned > stacknleft) {
1107 size_t len;
1108 size_t blocksize;
1109 struct stack_block *sp;
1110
1111 blocksize = aligned;
1112 if (blocksize < MINSIZE)
1113 blocksize = MINSIZE;
1114 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1115 if (len < blocksize)
1116 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1117 INT_OFF;
1118 sp = ckmalloc(len);
1119 sp->prev = stackp;
1120 stacknxt = sp->space;
1121 stacknleft = blocksize;
1122 sstrend = stacknxt + blocksize;
1123 stackp = sp;
1124 INT_ON;
1125 }
1126 p = stacknxt;
1127 stacknxt += aligned;
1128 stacknleft -= aligned;
1129 return p;
1130}
1131
1132static void
1133stunalloc(void *p)
1134{
1135#if DEBUG
1136 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
1137 write(2, "stunalloc\n", 10);
1138 abort();
1139 }
1140#endif
1141 stacknleft += stacknxt - (char *)p;
1142 stacknxt = p;
1143}
1144
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001145/*
1146 * Like strdup but works with the ash stack.
1147 */
1148static char *
1149ststrdup(const char *p)
1150{
1151 size_t len = strlen(p) + 1;
1152 return memcpy(stalloc(len), p, len);
1153}
1154
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001155static void
1156setstackmark(struct stackmark *mark)
1157{
1158 mark->stackp = stackp;
1159 mark->stacknxt = stacknxt;
1160 mark->stacknleft = stacknleft;
1161 mark->marknext = markp;
1162 markp = mark;
1163}
1164
1165static void
1166popstackmark(struct stackmark *mark)
1167{
1168 struct stack_block *sp;
1169
1170 INT_OFF;
1171 markp = mark->marknext;
1172 while (stackp != mark->stackp) {
1173 sp = stackp;
1174 stackp = sp->prev;
1175 free(sp);
1176 }
1177 stacknxt = mark->stacknxt;
1178 stacknleft = mark->stacknleft;
1179 sstrend = mark->stacknxt + mark->stacknleft;
1180 INT_ON;
1181}
1182
1183/*
1184 * When the parser reads in a string, it wants to stick the string on the
1185 * stack and only adjust the stack pointer when it knows how big the
1186 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1187 * of space on top of the stack and stackblocklen returns the length of
1188 * this block. Growstackblock will grow this space by at least one byte,
1189 * possibly moving it (like realloc). Grabstackblock actually allocates the
1190 * part of the block that has been used.
1191 */
1192static void
1193growstackblock(void)
1194{
1195 size_t newlen;
1196
1197 newlen = stacknleft * 2;
1198 if (newlen < stacknleft)
1199 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1200 if (newlen < 128)
1201 newlen += 128;
1202
1203 if (stacknxt == stackp->space && stackp != &stackbase) {
1204 struct stack_block *oldstackp;
1205 struct stackmark *xmark;
1206 struct stack_block *sp;
1207 struct stack_block *prevstackp;
1208 size_t grosslen;
1209
1210 INT_OFF;
1211 oldstackp = stackp;
1212 sp = stackp;
1213 prevstackp = sp->prev;
1214 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1215 sp = ckrealloc(sp, grosslen);
1216 sp->prev = prevstackp;
1217 stackp = sp;
1218 stacknxt = sp->space;
1219 stacknleft = newlen;
1220 sstrend = sp->space + newlen;
1221
1222 /*
1223 * Stack marks pointing to the start of the old block
1224 * must be relocated to point to the new block
1225 */
1226 xmark = markp;
1227 while (xmark != NULL && xmark->stackp == oldstackp) {
1228 xmark->stackp = stackp;
1229 xmark->stacknxt = stacknxt;
1230 xmark->stacknleft = stacknleft;
1231 xmark = xmark->marknext;
1232 }
1233 INT_ON;
1234 } else {
1235 char *oldspace = stacknxt;
1236 int oldlen = stacknleft;
1237 char *p = stalloc(newlen);
1238
1239 /* free the space we just allocated */
1240 stacknxt = memcpy(p, oldspace, oldlen);
1241 stacknleft += newlen;
1242 }
1243}
1244
1245static void
1246grabstackblock(size_t len)
1247{
1248 len = SHELL_ALIGN(len);
1249 stacknxt += len;
1250 stacknleft -= len;
1251}
1252
1253/*
1254 * The following routines are somewhat easier to use than the above.
1255 * The user declares a variable of type STACKSTR, which may be declared
1256 * to be a register. The macro STARTSTACKSTR initializes things. Then
1257 * the user uses the macro STPUTC to add characters to the string. In
1258 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1259 * grown as necessary. When the user is done, she can just leave the
1260 * string there and refer to it using stackblock(). Or she can allocate
1261 * the space for it using grabstackstr(). If it is necessary to allow
1262 * someone else to use the stack temporarily and then continue to grow
1263 * the string, the user should use grabstack to allocate the space, and
1264 * then call ungrabstr(p) to return to the previous mode of operation.
1265 *
1266 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1267 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1268 * is space for at least one character.
1269 */
1270static void *
1271growstackstr(void)
1272{
1273 size_t len = stackblocksize();
1274 if (herefd >= 0 && len >= 1024) {
1275 full_write(herefd, stackblock(), len);
1276 return stackblock();
1277 }
1278 growstackblock();
1279 return stackblock() + len;
1280}
1281
1282/*
1283 * Called from CHECKSTRSPACE.
1284 */
1285static char *
1286makestrspace(size_t newlen, char *p)
1287{
1288 size_t len = p - stacknxt;
1289 size_t size = stackblocksize();
1290
1291 for (;;) {
1292 size_t nleft;
1293
1294 size = stackblocksize();
1295 nleft = size - len;
1296 if (nleft >= newlen)
1297 break;
1298 growstackblock();
1299 }
1300 return stackblock() + len;
1301}
1302
1303static char *
1304stack_nputstr(const char *s, size_t n, char *p)
1305{
1306 p = makestrspace(n, p);
1307 p = memcpy(p, s, n) + n;
1308 return p;
1309}
1310
1311static char *
1312stack_putstr(const char *s, char *p)
1313{
1314 return stack_nputstr(s, strlen(s), p);
1315}
1316
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001317static char *
1318_STPUTC(int c, char *p)
1319{
1320 if (p == sstrend)
1321 p = growstackstr();
1322 *p++ = c;
1323 return p;
1324}
1325
1326#define STARTSTACKSTR(p) ((p) = stackblock())
1327#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
1328#define CHECKSTRSPACE(n, p) \
1329 ({ \
1330 char *q = (p); \
1331 size_t l = (n); \
1332 size_t m = sstrend - q; \
1333 if (l > m) \
1334 (p) = makestrspace(l, q); \
1335 0; \
1336 })
1337#define USTPUTC(c, p) (*p++ = (c))
1338#define STACKSTRNUL(p) ((p) == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0'))
1339#define STUNPUTC(p) (--p)
1340#define STTOPC(p) p[-1]
1341#define STADJUST(amount, p) (p += (amount))
1342
1343#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1344#define ungrabstackstr(s, p) stunalloc((s))
1345#define stackstrend() ((void *)sstrend)
1346
1347
1348/* ============ String helpers */
1349
1350/*
1351 * prefix -- see if pfx is a prefix of string.
1352 */
1353static char *
1354prefix(const char *string, const char *pfx)
1355{
1356 while (*pfx) {
1357 if (*pfx++ != *string++)
1358 return 0;
1359 }
1360 return (char *) string;
1361}
1362
1363/*
1364 * Check for a valid number. This should be elsewhere.
1365 */
1366static int
1367is_number(const char *p)
1368{
1369 do {
1370 if (!isdigit(*p))
1371 return 0;
1372 } while (*++p != '\0');
1373 return 1;
1374}
1375
1376/*
1377 * Convert a string of digits to an integer, printing an error message on
1378 * failure.
1379 */
1380static int
1381number(const char *s)
1382{
1383 if (!is_number(s))
1384 ash_msg_and_raise_error(illnum, s);
1385 return atoi(s);
1386}
1387
1388/*
1389 * Produce a possibly single quoted string suitable as input to the shell.
1390 * The return string is allocated on the stack.
1391 */
1392static char *
1393single_quote(const char *s)
1394{
1395 char *p;
1396
1397 STARTSTACKSTR(p);
1398
1399 do {
1400 char *q;
1401 size_t len;
1402
1403 len = strchrnul(s, '\'') - s;
1404
1405 q = p = makestrspace(len + 3, p);
1406
1407 *q++ = '\'';
1408 q = memcpy(q, s, len) + len;
1409 *q++ = '\'';
1410 s += len;
1411
1412 STADJUST(q - p, p);
1413
1414 len = strspn(s, "'");
1415 if (!len)
1416 break;
1417
1418 q = p = makestrspace(len + 3, p);
1419
1420 *q++ = '"';
1421 q = memcpy(q, s, len) + len;
1422 *q++ = '"';
1423 s += len;
1424
1425 STADJUST(q - p, p);
1426 } while (*s);
1427
1428 USTPUTC(0, p);
1429
1430 return stackblock();
1431}
1432
1433
1434/* ============ ... */
1435
1436static char **argptr; /* argument list for builtin commands */
1437static char *optionarg; /* set by nextopt (like getopt) */
1438static char *optptr; /* used by nextopt */
1439
1440/*
1441 * XXX - should get rid of. have all builtins use getopt(3). the
1442 * library getopt must have the BSD extension static variable "optreset"
1443 * otherwise it can't be used within the shell safely.
1444 *
1445 * Standard option processing (a la getopt) for builtin routines. The
1446 * only argument that is passed to nextopt is the option string; the
1447 * other arguments are unnecessary. It return the character, or '\0' on
1448 * end of input.
1449 */
1450static int
1451nextopt(const char *optstring)
1452{
1453 char *p;
1454 const char *q;
1455 char c;
1456
1457 p = optptr;
1458 if (p == NULL || *p == '\0') {
1459 p = *argptr;
1460 if (p == NULL || *p != '-' || *++p == '\0')
1461 return '\0';
1462 argptr++;
1463 if (LONE_DASH(p)) /* check for "--" */
1464 return '\0';
1465 }
1466 c = *p++;
1467 for (q = optstring; *q != c; ) {
1468 if (*q == '\0')
1469 ash_msg_and_raise_error("Illegal option -%c", c);
1470 if (*++q == ':')
1471 q++;
1472 }
1473 if (*++q == ':') {
1474 if (*p == '\0' && (p = *argptr++) == NULL)
1475 ash_msg_and_raise_error("No arg for -%c option", c);
1476 optionarg = p;
1477 p = NULL;
1478 }
1479 optptr = p;
1480 return c;
1481}
1482
1483
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001484/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001485
1486/* flags */
1487#define VEXPORT 0x01 /* variable is exported */
1488#define VREADONLY 0x02 /* variable cannot be modified */
1489#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1490#define VTEXTFIXED 0x08 /* text is statically allocated */
1491#define VSTACK 0x10 /* text is allocated on the stack */
1492#define VUNSET 0x20 /* the variable is not set */
1493#define VNOFUNC 0x40 /* don't call the callback function */
1494#define VNOSET 0x80 /* do not set variable - just readonly test */
1495#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1496#ifdef DYNAMIC_VAR
1497# define VDYNAMIC 0x200 /* dynamic variable */
1498# else
1499# define VDYNAMIC 0
1500#endif
1501
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001502static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1503#ifdef IFS_BROKEN
1504static const char defifsvar[] = "IFS= \t\n";
1505#define defifs (defifsvar + 4)
1506#else
1507static const char defifs[] = " \t\n";
1508#endif
1509
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001510struct shparam {
1511 int nparam; /* # of positional parameters (without $0) */
1512 unsigned char malloc; /* if parameter list dynamically allocated */
1513 char **p; /* parameter list */
1514#if ENABLE_ASH_GETOPTS
1515 int optind; /* next parameter to be processed by getopts */
1516 int optoff; /* used by getopts */
1517#endif
1518};
1519
1520static struct shparam shellparam; /* $@ current positional parameters */
1521
1522#if ENABLE_ASH_GETOPTS
1523static void
1524getoptsreset(const char *value)
1525{
1526 shellparam.optind = number(value);
1527 shellparam.optoff = -1;
1528}
1529#endif
1530
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001531struct var {
1532 struct var *next; /* next entry in hash list */
1533 int flags; /* flags are defined above */
1534 const char *text; /* name=value */
1535 void (*func)(const char *); /* function to be called when */
1536 /* the variable gets set/unset */
1537};
1538
1539struct localvar {
1540 struct localvar *next; /* next local variable in list */
1541 struct var *vp; /* the variable that was made local */
1542 int flags; /* saved flags */
1543 const char *text; /* saved text */
1544};
1545
1546/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001547#if ENABLE_LOCALE_SUPPORT
1548static void change_lc_all(const char *value);
1549static void change_lc_ctype(const char *value);
1550#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001551#if ENABLE_ASH_MAIL
1552static void chkmail(void);
1553static void changemail(const char *);
1554#endif
1555static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001556#if ENABLE_ASH_RANDOM_SUPPORT
1557static void change_random(const char *);
1558#endif
1559
1560static struct var varinit[] = {
1561#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001562 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001563#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001564 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001565#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001566#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001567 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1568 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001569#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001570 { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1571 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1572 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1573 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001574#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001575 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001576#endif
1577#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001578 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001579#endif
1580#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001581 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1582 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001583#endif
1584#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001585 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001586#endif
1587};
1588
1589#define vifs varinit[0]
1590#if ENABLE_ASH_MAIL
1591#define vmail (&vifs)[1]
1592#define vmpath (&vmail)[1]
1593#else
1594#define vmpath vifs
1595#endif
1596#define vpath (&vmpath)[1]
1597#define vps1 (&vpath)[1]
1598#define vps2 (&vps1)[1]
1599#define vps4 (&vps2)[1]
1600#define voptind (&vps4)[1]
1601#if ENABLE_ASH_GETOPTS
1602#define vrandom (&voptind)[1]
1603#else
1604#define vrandom (&vps4)[1]
1605#endif
1606#define defpath (defpathvar + 5)
1607
1608/*
1609 * The following macros access the values of the above variables.
1610 * They have to skip over the name. They return the null string
1611 * for unset variables.
1612 */
1613#define ifsval() (vifs.text + 4)
1614#define ifsset() ((vifs.flags & VUNSET) == 0)
1615#define mailval() (vmail.text + 5)
1616#define mpathval() (vmpath.text + 9)
1617#define pathval() (vpath.text + 5)
1618#define ps1val() (vps1.text + 4)
1619#define ps2val() (vps2.text + 4)
1620#define ps4val() (vps4.text + 4)
1621#define optindval() (voptind.text + 7)
1622
1623#define mpathset() ((vmpath.flags & VUNSET) == 0)
1624
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001625/*
1626 * The parsefile structure pointed to by the global variable parsefile
1627 * contains information about the current file being read.
1628 */
1629struct redirtab {
1630 struct redirtab *next;
1631 int renamed[10];
1632 int nullredirs;
1633};
1634
1635static struct redirtab *redirlist;
1636static int nullredirs;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001637extern char **environ;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001638static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1639
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001640#define VTABSIZE 39
1641
1642static struct var *vartab[VTABSIZE];
1643
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001644#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1645#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1646
1647/*
1648 * Return of a legal variable name (a letter or underscore followed by zero or
1649 * more letters, underscores, and digits).
1650 */
1651static char *
1652endofname(const char *name)
1653{
1654 char *p;
1655
1656 p = (char *) name;
1657 if (!is_name(*p))
1658 return p;
1659 while (*++p) {
1660 if (!is_in_name(*p))
1661 break;
1662 }
1663 return p;
1664}
1665
1666/*
1667 * Compares two strings up to the first = or '\0'. The first
1668 * string must be terminated by '='; the second may be terminated by
1669 * either '=' or '\0'.
1670 */
1671static int
1672varcmp(const char *p, const char *q)
1673{
1674 int c, d;
1675
1676 while ((c = *p) == (d = *q)) {
1677 if (!c || c == '=')
1678 goto out;
1679 p++;
1680 q++;
1681 }
1682 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001683 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001684 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001685 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001686 out:
1687 return c - d;
1688}
1689
1690static int
1691varequal(const char *a, const char *b)
1692{
1693 return !varcmp(a, b);
1694}
1695
1696/*
1697 * Find the appropriate entry in the hash table from the name.
1698 */
1699static struct var **
1700hashvar(const char *p)
1701{
1702 unsigned hashval;
1703
1704 hashval = ((unsigned char) *p) << 4;
1705 while (*p && *p != '=')
1706 hashval += (unsigned char) *p++;
1707 return &vartab[hashval % VTABSIZE];
1708}
1709
1710static int
1711vpcmp(const void *a, const void *b)
1712{
1713 return varcmp(*(const char **)a, *(const char **)b);
1714}
1715
1716/*
1717 * This routine initializes the builtin variables.
1718 */
1719static void
1720initvar(void)
1721{
1722 struct var *vp;
1723 struct var *end;
1724 struct var **vpp;
1725
1726 /*
1727 * PS1 depends on uid
1728 */
1729#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1730 vps1.text = "PS1=\\w \\$ ";
1731#else
1732 if (!geteuid())
1733 vps1.text = "PS1=# ";
1734#endif
1735 vp = varinit;
1736 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1737 do {
1738 vpp = hashvar(vp->text);
1739 vp->next = *vpp;
1740 *vpp = vp;
1741 } while (++vp < end);
1742}
1743
1744static struct var **
1745findvar(struct var **vpp, const char *name)
1746{
1747 for (; *vpp; vpp = &(*vpp)->next) {
1748 if (varequal((*vpp)->text, name)) {
1749 break;
1750 }
1751 }
1752 return vpp;
1753}
1754
1755/*
1756 * Find the value of a variable. Returns NULL if not set.
1757 */
1758static char *
1759lookupvar(const char *name)
1760{
1761 struct var *v;
1762
1763 v = *findvar(hashvar(name), name);
1764 if (v) {
1765#ifdef DYNAMIC_VAR
1766 /*
1767 * Dynamic variables are implemented roughly the same way they are
1768 * in bash. Namely, they're "special" so long as they aren't unset.
1769 * As soon as they're unset, they're no longer dynamic, and dynamic
1770 * lookup will no longer happen at that point. -- PFM.
1771 */
1772 if ((v->flags & VDYNAMIC))
1773 (*v->func)(NULL);
1774#endif
1775 if (!(v->flags & VUNSET))
1776 return strchrnul(v->text, '=') + 1;
1777 }
1778 return NULL;
1779}
1780
1781/*
1782 * Search the environment of a builtin command.
1783 */
1784static char *
1785bltinlookup(const char *name)
1786{
1787 struct strlist *sp;
1788
1789 for (sp = cmdenviron; sp; sp = sp->next) {
1790 if (varequal(sp->text, name))
1791 return strchrnul(sp->text, '=') + 1;
1792 }
1793 return lookupvar(name);
1794}
1795
1796/*
1797 * Same as setvar except that the variable and value are passed in
1798 * the first argument as name=value. Since the first argument will
1799 * be actually stored in the table, it should not be a string that
1800 * will go away.
1801 * Called with interrupts off.
1802 */
1803static void
1804setvareq(char *s, int flags)
1805{
1806 struct var *vp, **vpp;
1807
1808 vpp = hashvar(s);
1809 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1810 vp = *findvar(vpp, s);
1811 if (vp) {
1812 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1813 const char *n;
1814
1815 if (flags & VNOSAVE)
1816 free(s);
1817 n = vp->text;
1818 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1819 }
1820
1821 if (flags & VNOSET)
1822 return;
1823
1824 if (vp->func && (flags & VNOFUNC) == 0)
1825 (*vp->func)(strchrnul(s, '=') + 1);
1826
1827 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1828 free((char*)vp->text);
1829
1830 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1831 } else {
1832 if (flags & VNOSET)
1833 return;
1834 /* not found */
1835 vp = ckmalloc(sizeof(*vp));
1836 vp->next = *vpp;
1837 vp->func = NULL;
1838 *vpp = vp;
1839 }
1840 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1841 s = ckstrdup(s);
1842 vp->text = s;
1843 vp->flags = flags;
1844}
1845
1846/*
1847 * Set the value of a variable. The flags argument is ored with the
1848 * flags of the variable. If val is NULL, the variable is unset.
1849 */
1850static void
1851setvar(const char *name, const char *val, int flags)
1852{
1853 char *p, *q;
1854 size_t namelen;
1855 char *nameeq;
1856 size_t vallen;
1857
1858 q = endofname(name);
1859 p = strchrnul(q, '=');
1860 namelen = p - name;
1861 if (!namelen || p != q)
1862 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1863 vallen = 0;
1864 if (val == NULL) {
1865 flags |= VUNSET;
1866 } else {
1867 vallen = strlen(val);
1868 }
1869 INT_OFF;
1870 nameeq = ckmalloc(namelen + vallen + 2);
1871 p = memcpy(nameeq, name, namelen) + namelen;
1872 if (val) {
1873 *p++ = '=';
1874 p = memcpy(p, val, vallen) + vallen;
1875 }
1876 *p = '\0';
1877 setvareq(nameeq, flags | VNOSAVE);
1878 INT_ON;
1879}
1880
1881#if ENABLE_ASH_GETOPTS
1882/*
1883 * Safe version of setvar, returns 1 on success 0 on failure.
1884 */
1885static int
1886setvarsafe(const char *name, const char *val, int flags)
1887{
1888 int err;
1889 volatile int saveint;
1890 struct jmploc *volatile savehandler = exception_handler;
1891 struct jmploc jmploc;
1892
1893 SAVE_INT(saveint);
1894 if (setjmp(jmploc.loc))
1895 err = 1;
1896 else {
1897 exception_handler = &jmploc;
1898 setvar(name, val, flags);
1899 err = 0;
1900 }
1901 exception_handler = savehandler;
1902 RESTORE_INT(saveint);
1903 return err;
1904}
1905#endif
1906
1907/*
1908 * Unset the specified variable.
1909 */
1910static int
1911unsetvar(const char *s)
1912{
1913 struct var **vpp;
1914 struct var *vp;
1915 int retval;
1916
1917 vpp = findvar(hashvar(s), s);
1918 vp = *vpp;
1919 retval = 2;
1920 if (vp) {
1921 int flags = vp->flags;
1922
1923 retval = 1;
1924 if (flags & VREADONLY)
1925 goto out;
1926#ifdef DYNAMIC_VAR
1927 vp->flags &= ~VDYNAMIC;
1928#endif
1929 if (flags & VUNSET)
1930 goto ok;
1931 if ((flags & VSTRFIXED) == 0) {
1932 INT_OFF;
1933 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
1934 free((char*)vp->text);
1935 *vpp = vp->next;
1936 free(vp);
1937 INT_ON;
1938 } else {
1939 setvar(s, 0, 0);
1940 vp->flags &= ~VEXPORT;
1941 }
1942 ok:
1943 retval = 0;
1944 }
1945 out:
1946 return retval;
1947}
1948
1949/*
1950 * Process a linked list of variable assignments.
1951 */
1952static void
1953listsetvar(struct strlist *list_set_var, int flags)
1954{
1955 struct strlist *lp = list_set_var;
1956
1957 if (!lp)
1958 return;
1959 INT_OFF;
1960 do {
1961 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00001962 lp = lp->next;
1963 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964 INT_ON;
1965}
1966
1967/*
1968 * Generate a list of variables satisfying the given conditions.
1969 */
1970static char **
1971listvars(int on, int off, char ***end)
1972{
1973 struct var **vpp;
1974 struct var *vp;
1975 char **ep;
1976 int mask;
1977
1978 STARTSTACKSTR(ep);
1979 vpp = vartab;
1980 mask = on | off;
1981 do {
1982 for (vp = *vpp; vp; vp = vp->next) {
1983 if ((vp->flags & mask) == on) {
1984 if (ep == stackstrend())
1985 ep = growstackstr();
1986 *ep++ = (char *) vp->text;
1987 }
1988 }
1989 } while (++vpp < vartab + VTABSIZE);
1990 if (ep == stackstrend())
1991 ep = growstackstr();
1992 if (end)
1993 *end = ep;
1994 *ep++ = NULL;
1995 return grabstackstr(ep);
1996}
1997
1998
1999/* ============ Path search helper
2000 *
2001 * The variable path (passed by reference) should be set to the start
2002 * of the path before the first call; padvance will update
2003 * this value as it proceeds. Successive calls to padvance will return
2004 * the possible path expansions in sequence. If an option (indicated by
2005 * a percent sign) appears in the path entry then the global variable
2006 * pathopt will be set to point to it; otherwise pathopt will be set to
2007 * NULL.
2008 */
2009static const char *pathopt; /* set by padvance */
2010
2011static char *
2012padvance(const char **path, const char *name)
2013{
2014 const char *p;
2015 char *q;
2016 const char *start;
2017 size_t len;
2018
2019 if (*path == NULL)
2020 return NULL;
2021 start = *path;
2022 for (p = start; *p && *p != ':' && *p != '%'; p++);
2023 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2024 while (stackblocksize() < len)
2025 growstackblock();
2026 q = stackblock();
2027 if (p != start) {
2028 memcpy(q, start, p - start);
2029 q += p - start;
2030 *q++ = '/';
2031 }
2032 strcpy(q, name);
2033 pathopt = NULL;
2034 if (*p == '%') {
2035 pathopt = ++p;
2036 while (*p && *p != ':') p++;
2037 }
2038 if (*p == ':')
2039 *path = p + 1;
2040 else
2041 *path = NULL;
2042 return stalloc(len);
2043}
2044
2045
2046/* ============ Prompt */
2047
2048static int doprompt; /* if set, prompt the user */
2049static int needprompt; /* true if interactive and at start of line */
2050
2051#if ENABLE_FEATURE_EDITING
2052static line_input_t *line_input_state;
2053static const char *cmdedit_prompt;
2054static void
2055putprompt(const char *s)
2056{
2057 if (ENABLE_ASH_EXPAND_PRMT) {
2058 free((char*)cmdedit_prompt);
2059 cmdedit_prompt = xstrdup(s);
2060 return;
2061 }
2062 cmdedit_prompt = s;
2063}
2064#else
2065static void
2066putprompt(const char *s)
2067{
2068 out2str(s);
2069}
2070#endif
2071
2072#if ENABLE_ASH_EXPAND_PRMT
2073/* expandstr() needs parsing machinery, so it is far away ahead... */
2074static const char *expandstr(const char *ps);
2075#else
2076#define expandstr(s) s
2077#endif
2078
2079static void
2080setprompt(int whichprompt)
2081{
2082 const char *prompt;
2083#if ENABLE_ASH_EXPAND_PRMT
2084 struct stackmark smark;
2085#endif
2086
2087 needprompt = 0;
2088
2089 switch (whichprompt) {
2090 case 1:
2091 prompt = ps1val();
2092 break;
2093 case 2:
2094 prompt = ps2val();
2095 break;
2096 default: /* 0 */
2097 prompt = nullstr;
2098 }
2099#if ENABLE_ASH_EXPAND_PRMT
2100 setstackmark(&smark);
2101 stalloc(stackblocksize());
2102#endif
2103 putprompt(expandstr(prompt));
2104#if ENABLE_ASH_EXPAND_PRMT
2105 popstackmark(&smark);
2106#endif
2107}
2108
2109
2110/* ============ The cd and pwd commands */
2111
2112#define CD_PHYSICAL 1
2113#define CD_PRINT 2
2114
2115static int docd(const char *, int);
2116
2117static char *curdir = nullstr; /* current working directory */
2118static char *physdir = nullstr; /* physical working directory */
2119
2120static int
2121cdopt(void)
2122{
2123 int flags = 0;
2124 int i, j;
2125
2126 j = 'L';
2127 while ((i = nextopt("LP"))) {
2128 if (i != j) {
2129 flags ^= CD_PHYSICAL;
2130 j = i;
2131 }
2132 }
2133
2134 return flags;
2135}
2136
2137/*
2138 * Update curdir (the name of the current directory) in response to a
2139 * cd command.
2140 */
2141static const char *
2142updatepwd(const char *dir)
2143{
2144 char *new;
2145 char *p;
2146 char *cdcomppath;
2147 const char *lim;
2148
2149 cdcomppath = ststrdup(dir);
2150 STARTSTACKSTR(new);
2151 if (*dir != '/') {
2152 if (curdir == nullstr)
2153 return 0;
2154 new = stack_putstr(curdir, new);
2155 }
2156 new = makestrspace(strlen(dir) + 2, new);
2157 lim = stackblock() + 1;
2158 if (*dir != '/') {
2159 if (new[-1] != '/')
2160 USTPUTC('/', new);
2161 if (new > lim && *lim == '/')
2162 lim++;
2163 } else {
2164 USTPUTC('/', new);
2165 cdcomppath++;
2166 if (dir[1] == '/' && dir[2] != '/') {
2167 USTPUTC('/', new);
2168 cdcomppath++;
2169 lim++;
2170 }
2171 }
2172 p = strtok(cdcomppath, "/");
2173 while (p) {
2174 switch (*p) {
2175 case '.':
2176 if (p[1] == '.' && p[2] == '\0') {
2177 while (new > lim) {
2178 STUNPUTC(new);
2179 if (new[-1] == '/')
2180 break;
2181 }
2182 break;
2183 } else if (p[1] == '\0')
2184 break;
2185 /* fall through */
2186 default:
2187 new = stack_putstr(p, new);
2188 USTPUTC('/', new);
2189 }
2190 p = strtok(0, "/");
2191 }
2192 if (new > lim)
2193 STUNPUTC(new);
2194 *new = 0;
2195 return stackblock();
2196}
2197
2198/*
2199 * Find out what the current directory is. If we already know the current
2200 * directory, this routine returns immediately.
2201 */
2202static char *
2203getpwd(void)
2204{
2205 char *dir = getcwd(0, 0);
2206 return dir ? dir : nullstr;
2207}
2208
2209static void
2210setpwd(const char *val, int setold)
2211{
2212 char *oldcur, *dir;
2213
2214 oldcur = dir = curdir;
2215
2216 if (setold) {
2217 setvar("OLDPWD", oldcur, VEXPORT);
2218 }
2219 INT_OFF;
2220 if (physdir != nullstr) {
2221 if (physdir != oldcur)
2222 free(physdir);
2223 physdir = nullstr;
2224 }
2225 if (oldcur == val || !val) {
2226 char *s = getpwd();
2227 physdir = s;
2228 if (!val)
2229 dir = s;
2230 } else
2231 dir = ckstrdup(val);
2232 if (oldcur != dir && oldcur != nullstr) {
2233 free(oldcur);
2234 }
2235 curdir = dir;
2236 INT_ON;
2237 setvar("PWD", dir, VEXPORT);
2238}
2239
2240static void hashcd(void);
2241
2242/*
2243 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2244 * know that the current directory has changed.
2245 */
2246static int
2247docd(const char *dest, int flags)
2248{
2249 const char *dir = 0;
2250 int err;
2251
2252 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2253
2254 INT_OFF;
2255 if (!(flags & CD_PHYSICAL)) {
2256 dir = updatepwd(dest);
2257 if (dir)
2258 dest = dir;
2259 }
2260 err = chdir(dest);
2261 if (err)
2262 goto out;
2263 setpwd(dir, 1);
2264 hashcd();
2265 out:
2266 INT_ON;
2267 return err;
2268}
2269
2270static int
2271cdcmd(int argc, char **argv)
2272{
2273 const char *dest;
2274 const char *path;
2275 const char *p;
2276 char c;
2277 struct stat statb;
2278 int flags;
2279
2280 flags = cdopt();
2281 dest = *argptr;
2282 if (!dest)
2283 dest = bltinlookup(homestr);
2284 else if (LONE_DASH(dest)) {
2285 dest = bltinlookup("OLDPWD");
2286 flags |= CD_PRINT;
2287 }
2288 if (!dest)
2289 dest = nullstr;
2290 if (*dest == '/')
2291 goto step7;
2292 if (*dest == '.') {
2293 c = dest[1];
2294 dotdot:
2295 switch (c) {
2296 case '\0':
2297 case '/':
2298 goto step6;
2299 case '.':
2300 c = dest[2];
2301 if (c != '.')
2302 goto dotdot;
2303 }
2304 }
2305 if (!*dest)
2306 dest = ".";
2307 path = bltinlookup("CDPATH");
2308 if (!path) {
2309 step6:
2310 step7:
2311 p = dest;
2312 goto docd;
2313 }
2314 do {
2315 c = *path;
2316 p = padvance(&path, dest);
2317 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2318 if (c && c != ':')
2319 flags |= CD_PRINT;
2320 docd:
2321 if (!docd(p, flags))
2322 goto out;
2323 break;
2324 }
2325 } while (path);
2326 ash_msg_and_raise_error("can't cd to %s", dest);
2327 /* NOTREACHED */
2328 out:
2329 if (flags & CD_PRINT)
2330 out1fmt(snlfmt, curdir);
2331 return 0;
2332}
2333
2334static int
2335pwdcmd(int argc, char **argv)
2336{
2337 int flags;
2338 const char *dir = curdir;
2339
2340 flags = cdopt();
2341 if (flags) {
2342 if (physdir == nullstr)
2343 setpwd(dir, 0);
2344 dir = physdir;
2345 }
2346 out1fmt(snlfmt, dir);
2347 return 0;
2348}
2349
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002350
Denis Vlasenkob012b102007-02-19 22:43:01 +00002351/* ============ Unsorted yet */
2352
2353
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002354/* expand.h */
Eric Andersen2870d962001-07-02 17:27:21 +00002355
Eric Andersen2870d962001-07-02 17:27:21 +00002356struct arglist {
2357 struct strlist *list;
2358 struct strlist **lastp;
2359};
2360
Eric Andersenc470f442003-07-28 09:56:35 +00002361/*
2362 * expandarg() flags
2363 */
2364#define EXP_FULL 0x1 /* perform word splitting & file globbing */
2365#define EXP_TILDE 0x2 /* do normal tilde expansion */
2366#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
2367#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
2368#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
2369#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
2370#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
2371#define EXP_WORD 0x80 /* expand word in parameter expansion */
2372#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
2373
2374
Eric Andersenc470f442003-07-28 09:56:35 +00002375static void expandarg(union node *, struct arglist *, int);
2376#define rmescapes(p) _rmescapes((p), 0)
2377static char *_rmescapes(char *, int);
2378static int casematch(union node *, char *);
2379
Denis Vlasenko131ae172007-02-18 13:00:19 +00002380#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00002381static void expari(int);
Eric Andersen2870d962001-07-02 17:27:21 +00002382#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002383
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002384
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002385/* parser.h */
Eric Andersenc470f442003-07-28 09:56:35 +00002386
2387/* control characters in argument strings */
2388#define CTL_FIRST '\201' /* first 'special' character */
2389#define CTLESC '\201' /* escape next character */
2390#define CTLVAR '\202' /* variable defn */
2391#define CTLENDVAR '\203'
2392#define CTLBACKQ '\204'
2393#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
2394/* CTLBACKQ | CTLQUOTE == '\205' */
2395#define CTLARI '\206' /* arithmetic expression */
2396#define CTLENDARI '\207'
2397#define CTLQUOTEMARK '\210'
2398#define CTL_LAST '\210' /* last 'special' character */
2399
2400/* variable substitution byte (follows CTLVAR) */
2401#define VSTYPE 0x0f /* type of variable substitution */
2402#define VSNUL 0x10 /* colon--treat the empty string as unset */
2403#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
2404
2405/* values of VSTYPE field */
2406#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
2407#define VSMINUS 0x2 /* ${var-text} */
2408#define VSPLUS 0x3 /* ${var+text} */
2409#define VSQUESTION 0x4 /* ${var?message} */
2410#define VSASSIGN 0x5 /* ${var=text} */
2411#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
2412#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
2413#define VSTRIMLEFT 0x8 /* ${var#pattern} */
2414#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
2415#define VSLENGTH 0xa /* ${#var} */
2416
2417/* values of checkkwd variable */
2418#define CHKALIAS 0x1
2419#define CHKKWD 0x2
2420#define CHKNL 0x4
2421
2422#define IBUFSIZ (BUFSIZ + 1)
2423
2424/*
2425 * NEOF is returned by parsecmd when it encounters an end of file. It
2426 * must be distinct from NULL, so we use the address of a variable that
2427 * happens to be handy.
2428 */
2429static int plinno = 1; /* input line number */
2430
2431/* number of characters left in input buffer */
2432static int parsenleft; /* copy of parsefile->nleft */
2433static int parselleft; /* copy of parsefile->lleft */
2434
2435/* next character in input buffer */
Eric Andersen90898442003-08-06 11:20:52 +00002436static char *parsenextc; /* copy of parsefile->nextc */
Glenn L McGrathd3612172003-09-17 00:22:26 +00002437
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002438#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002439
Eric Andersenc470f442003-07-28 09:56:35 +00002440static int tokpushback; /* last token pushed back */
2441#define NEOF ((union node *)&tokpushback)
2442static int parsebackquote; /* nonzero if we are inside backquotes */
Eric Andersenc470f442003-07-28 09:56:35 +00002443static int lasttoken; /* last token read */
2444static char *wordtext; /* text of last word returned by readtoken */
2445static int checkkwd;
2446static struct nodelist *backquotelist;
2447static union node *redirnode;
2448static struct heredoc *heredoc;
2449static int quoteflag; /* set if (part of) last token was quoted */
Eric Andersenc470f442003-07-28 09:56:35 +00002450
Eric Andersenc470f442003-07-28 09:56:35 +00002451static void fixredir(union node *, const char *, int);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002452
Eric Andersenc470f442003-07-28 09:56:35 +00002453
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002454/* shell.h */
Eric Andersenc470f442003-07-28 09:56:35 +00002455
Eric Andersenc470f442003-07-28 09:56:35 +00002456static const char spcstr[] = " ";
Eric Andersenc470f442003-07-28 09:56:35 +00002457static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
Eric Andersen2870d962001-07-02 17:27:21 +00002458
Eric Andersenc470f442003-07-28 09:56:35 +00002459#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
2460#define __builtin_expect(x, expected_value) (x)
2461#endif
2462
Glenn L McGrath2f325a02004-08-06 01:49:04 +00002463#define xlikely(x) __builtin_expect((x),1)
Eric Andersen90898442003-08-06 11:20:52 +00002464
Eric Andersenc470f442003-07-28 09:56:35 +00002465#define TEOF 0
2466#define TNL 1
2467#define TREDIR 2
2468#define TWORD 3
2469#define TSEMI 4
2470#define TBACKGND 5
2471#define TAND 6
2472#define TOR 7
2473#define TPIPE 8
2474#define TLP 9
2475#define TRP 10
2476#define TENDCASE 11
2477#define TENDBQUOTE 12
2478#define TNOT 13
2479#define TCASE 14
2480#define TDO 15
2481#define TDONE 16
2482#define TELIF 17
2483#define TELSE 18
2484#define TESAC 19
2485#define TFI 20
2486#define TFOR 21
2487#define TIF 22
2488#define TIN 23
2489#define TTHEN 24
2490#define TUNTIL 25
2491#define TWHILE 26
2492#define TBEGIN 27
2493#define TEND 28
2494
2495/* first char is indicating which tokens mark the end of a list */
2496static const char *const tokname_array[] = {
2497 "\1end of file",
2498 "\0newline",
2499 "\0redirection",
2500 "\0word",
2501 "\0;",
2502 "\0&",
2503 "\0&&",
2504 "\0||",
2505 "\0|",
2506 "\0(",
2507 "\1)",
2508 "\1;;",
2509 "\1`",
2510#define KWDOFFSET 13
2511 /* the following are keywords */
2512 "\0!",
2513 "\0case",
2514 "\1do",
2515 "\1done",
2516 "\1elif",
2517 "\1else",
2518 "\1esac",
2519 "\1fi",
2520 "\0for",
2521 "\0if",
2522 "\0in",
2523 "\1then",
2524 "\0until",
2525 "\0while",
2526 "\0{",
2527 "\1}",
2528};
2529
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002530static const char *
2531tokname(int tok)
Eric Andersenc470f442003-07-28 09:56:35 +00002532{
2533 static char buf[16];
2534
2535 if (tok >= TSEMI)
2536 buf[0] = '"';
2537 sprintf(buf + (tok >= TSEMI), "%s%c",
2538 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
2539 return buf;
2540}
2541
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002542/* Wrapper around strcmp for qsort/bsearch/... */
2543static int
2544pstrcmp(const void *a, const void *b)
2545{
2546 return strcmp((const char *) a, (*(const char *const *) b) + 1);
2547}
Eric Andersenc470f442003-07-28 09:56:35 +00002548
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002549static const char *const *
2550findkwd(const char *s)
2551{
2552 return bsearch(s, tokname_array + KWDOFFSET,
2553 (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
2554 sizeof(const char *), pstrcmp);
2555}
Eric Andersenc470f442003-07-28 09:56:35 +00002556
2557/* Syntax classes */
2558#define CWORD 0 /* character is nothing special */
2559#define CNL 1 /* newline character */
2560#define CBACK 2 /* a backslash character */
2561#define CSQUOTE 3 /* single quote */
2562#define CDQUOTE 4 /* double quote */
2563#define CENDQUOTE 5 /* a terminating quote */
2564#define CBQUOTE 6 /* backwards single quote */
2565#define CVAR 7 /* a dollar sign */
2566#define CENDVAR 8 /* a '}' character */
2567#define CLP 9 /* a left paren in arithmetic */
2568#define CRP 10 /* a right paren in arithmetic */
2569#define CENDFILE 11 /* end of file */
2570#define CCTL 12 /* like CWORD, except it must be escaped */
2571#define CSPCL 13 /* these terminate a word */
2572#define CIGN 14 /* character should be ignored */
2573
Denis Vlasenko131ae172007-02-18 13:00:19 +00002574#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002575#define SYNBASE 130
2576#define PEOF -130
2577#define PEOA -129
2578#define PEOA_OR_PEOF PEOA
2579#else
2580#define SYNBASE 129
2581#define PEOF -129
2582#define PEOA_OR_PEOF PEOF
2583#endif
2584
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00002585/* C99 say: "char" declaration may be signed or unsigned default */
2586#define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int)
2587
Eric Andersenc470f442003-07-28 09:56:35 +00002588/*
2589 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
2590 * (assuming ascii char codes, as the original implementation did)
2591 */
2592#define is_special(c) \
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002593 ( (((unsigned int)c) - 33 < 32) \
Eric Andersenc470f442003-07-28 09:56:35 +00002594 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
2595
Denis Vlasenko131ae172007-02-18 13:00:19 +00002596#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002597#define USE_SIT_FUNCTION
2598#endif
2599
2600/* number syntax index */
Eric Andersenc470f442003-07-28 09:56:35 +00002601#define BASESYNTAX 0 /* not in quotes */
2602#define DQSYNTAX 1 /* in double quotes */
2603#define SQSYNTAX 2 /* in single quotes */
2604#define ARISYNTAX 3 /* in arithmetic */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002605
Denis Vlasenko131ae172007-02-18 13:00:19 +00002606#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002607static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002608#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002609 {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
2610#endif
2611 {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
2612 {CNL, CNL, CNL, CNL}, /* 2, \n */
2613 {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
2614 {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
2615 {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
2616 {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
2617 {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
2618 {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
2619 {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
2620 {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
2621 {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002622#ifndef USE_SIT_FUNCTION
Eric Andersenc470f442003-07-28 09:56:35 +00002623 {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
2624 {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
2625 {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002626#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002627};
Eric Andersenc470f442003-07-28 09:56:35 +00002628#else
2629static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002630#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002631 {CSPCL, CIGN, CIGN}, /* 0, PEOA */
2632#endif
2633 {CSPCL, CWORD, CWORD}, /* 1, ' ' */
2634 {CNL, CNL, CNL}, /* 2, \n */
2635 {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
2636 {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
2637 {CVAR, CVAR, CWORD}, /* 5, $ */
2638 {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
2639 {CSPCL, CWORD, CWORD}, /* 7, ( */
2640 {CSPCL, CWORD, CWORD}, /* 8, ) */
2641 {CBACK, CBACK, CCTL}, /* 9, \ */
2642 {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
2643 {CENDVAR, CENDVAR, CWORD}, /* 11, } */
2644#ifndef USE_SIT_FUNCTION
2645 {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
2646 {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
2647 {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
2648#endif
2649};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002650#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002651
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002652#ifdef USE_SIT_FUNCTION
2653
2654#define U_C(c) ((unsigned char)(c))
2655
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002656static int
2657SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002658{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002659 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002660#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002661 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002662 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2663 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2664 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2665 11, 3 /* "}~" */
2666 };
2667#else
2668 static const char syntax_index_table[] = {
2669 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2670 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2671 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2672 10, 2 /* "}~" */
2673 };
2674#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002675 const char *s;
2676 int indx;
2677
Eric Andersenc470f442003-07-28 09:56:35 +00002678 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002679 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002680#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002681 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002682 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002683 else
2684#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002685 if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
2686 return CCTL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002687 else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002688 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002689 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002690 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002691 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002692 }
2693 return S_I_T[indx][syntax];
2694}
2695
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00002696#else /* USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002697
2698#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
2699
Denis Vlasenko131ae172007-02-18 13:00:19 +00002700#if ENABLE_ASH_ALIAS
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002701#define CSPCL_CIGN_CIGN_CIGN 0
2702#define CSPCL_CWORD_CWORD_CWORD 1
2703#define CNL_CNL_CNL_CNL 2
2704#define CWORD_CCTL_CCTL_CWORD 3
Eric Andersenc470f442003-07-28 09:56:35 +00002705#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002706#define CVAR_CVAR_CWORD_CVAR 5
Eric Andersenc470f442003-07-28 09:56:35 +00002707#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002708#define CSPCL_CWORD_CWORD_CLP 7
2709#define CSPCL_CWORD_CWORD_CRP 8
2710#define CBACK_CBACK_CCTL_CBACK 9
2711#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2712#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2713#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2714#define CWORD_CWORD_CWORD_CWORD 13
2715#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002716#else
2717#define CSPCL_CWORD_CWORD_CWORD 0
2718#define CNL_CNL_CNL_CNL 1
2719#define CWORD_CCTL_CCTL_CWORD 2
2720#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2721#define CVAR_CVAR_CWORD_CVAR 4
2722#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2723#define CSPCL_CWORD_CWORD_CLP 6
2724#define CSPCL_CWORD_CWORD_CRP 7
2725#define CBACK_CBACK_CCTL_CBACK 8
2726#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2727#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2728#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2729#define CWORD_CWORD_CWORD_CWORD 12
2730#define CCTL_CCTL_CCTL_CCTL 13
2731#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002732
2733static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002734 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002735 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002736#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002737 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2738#endif
2739 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2741 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2742 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2743 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2744 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2745 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2746 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2747 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002748 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2878 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2900 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002901 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002902 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2904 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002906 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002907 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2908 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2909 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2910 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2912 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2914 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2915 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2926 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2927 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2928 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2929 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2930 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2931 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2959 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2960 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2961 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2964 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2992 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2993 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2994 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002995};
2996
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002997#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002998
Eric Andersen2870d962001-07-02 17:27:21 +00002999
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003000/* alias.c */
Eric Andersen2870d962001-07-02 17:27:21 +00003001
Eric Andersenc470f442003-07-28 09:56:35 +00003002#define ATABSIZE 39
3003
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003004static int funcblocksize; /* size of structures in function */
3005static int funcstringsize; /* size of strings in node */
3006static void *funcblock; /* block to allocate function from */
3007static char *funcstring; /* block to allocate strings from */
Eric Andersenc470f442003-07-28 09:56:35 +00003008
3009static const short nodesize[26] = {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003010 SHELL_ALIGN(sizeof(struct ncmd)),
3011 SHELL_ALIGN(sizeof(struct npipe)),
3012 SHELL_ALIGN(sizeof(struct nredir)),
3013 SHELL_ALIGN(sizeof(struct nredir)),
3014 SHELL_ALIGN(sizeof(struct nredir)),
3015 SHELL_ALIGN(sizeof(struct nbinary)),
3016 SHELL_ALIGN(sizeof(struct nbinary)),
3017 SHELL_ALIGN(sizeof(struct nbinary)),
3018 SHELL_ALIGN(sizeof(struct nif)),
3019 SHELL_ALIGN(sizeof(struct nbinary)),
3020 SHELL_ALIGN(sizeof(struct nbinary)),
3021 SHELL_ALIGN(sizeof(struct nfor)),
3022 SHELL_ALIGN(sizeof(struct ncase)),
3023 SHELL_ALIGN(sizeof(struct nclist)),
3024 SHELL_ALIGN(sizeof(struct narg)),
3025 SHELL_ALIGN(sizeof(struct narg)),
3026 SHELL_ALIGN(sizeof(struct nfile)),
3027 SHELL_ALIGN(sizeof(struct nfile)),
3028 SHELL_ALIGN(sizeof(struct nfile)),
3029 SHELL_ALIGN(sizeof(struct nfile)),
3030 SHELL_ALIGN(sizeof(struct nfile)),
3031 SHELL_ALIGN(sizeof(struct ndup)),
3032 SHELL_ALIGN(sizeof(struct ndup)),
3033 SHELL_ALIGN(sizeof(struct nhere)),
3034 SHELL_ALIGN(sizeof(struct nhere)),
3035 SHELL_ALIGN(sizeof(struct nnot)),
Eric Andersen2870d962001-07-02 17:27:21 +00003036};
3037
Eric Andersenc470f442003-07-28 09:56:35 +00003038static void calcsize(union node *);
3039static void sizenodelist(struct nodelist *);
3040static union node *copynode(union node *);
3041static struct nodelist *copynodelist(struct nodelist *);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003042static char *nodeckstrdup(char *);
Eric Andersen2870d962001-07-02 17:27:21 +00003043
Eric Andersenc470f442003-07-28 09:56:35 +00003044static int evalskip; /* set if we are skipping commands */
3045static int skipcount; /* number of levels to skip */
3046static int funcnest; /* depth of function calls */
Eric Andersen2870d962001-07-02 17:27:21 +00003047
3048/* reasons for skipping commands (see comment on breakcmd routine) */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003049#define SKIPBREAK (1 << 0)
3050#define SKIPCONT (1 << 1)
3051#define SKIPFUNC (1 << 2)
3052#define SKIPFILE (1 << 3)
3053#define SKIPEVAL (1 << 4)
Eric Andersen2870d962001-07-02 17:27:21 +00003054
Eric Andersenc470f442003-07-28 09:56:35 +00003055
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003056/* exec.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003057
3058/* values of cmdtype */
3059#define CMDUNKNOWN -1 /* no entry in table for command */
3060#define CMDNORMAL 0 /* command is an executable program */
3061#define CMDFUNCTION 1 /* command is a shell function */
3062#define CMDBUILTIN 2 /* command is a shell builtin */
3063
3064struct builtincmd {
3065 const char *name;
3066 int (*builtin)(int, char **);
3067 /* unsigned flags; */
3068};
3069
Eric Andersenc470f442003-07-28 09:56:35 +00003070struct cmdentry {
3071 int cmdtype;
3072 union param {
3073 int index;
3074 const struct builtincmd *cmd;
3075 struct funcnode *func;
3076 } u;
3077};
3078
Eric Andersenc470f442003-07-28 09:56:35 +00003079/* action to find_command() */
3080#define DO_ERR 0x01 /* prints errors */
3081#define DO_ABS 0x02 /* checks absolute paths */
3082#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
3083#define DO_ALTPATH 0x08 /* using alternate path */
3084#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
3085
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003086static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
Eric Andersenc470f442003-07-28 09:56:35 +00003087static char *padvance(const char **, const char *);
3088static void find_command(char *, struct cmdentry *, int, const char *);
3089static struct builtincmd *find_builtin(const char *);
Eric Andersenc470f442003-07-28 09:56:35 +00003090static void defun(char *, union node *);
3091static void unsetfunc(const char *);
3092
Denis Vlasenko131ae172007-02-18 13:00:19 +00003093#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersened9ecf72004-06-22 08:29:45 +00003094typedef int64_t arith_t;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00003095#define arith_t_type long long
Eric Andersened9ecf72004-06-22 08:29:45 +00003096#else
3097typedef long arith_t;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00003098#define arith_t_type long
Eric Andersened9ecf72004-06-22 08:29:45 +00003099#endif
Glenn L McGrath5f2a23c2004-06-25 07:05:13 +00003100
Denis Vlasenko131ae172007-02-18 13:00:19 +00003101#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +00003102static arith_t dash_arith(const char *);
3103static arith_t arith(const char *expr, int *perrcode);
Eric Andersenc470f442003-07-28 09:56:35 +00003104#endif
3105
Denis Vlasenko131ae172007-02-18 13:00:19 +00003106#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersen16767e22004-03-16 05:14:10 +00003107static unsigned long rseed;
Eric Andersen16767e22004-03-16 05:14:10 +00003108# ifndef DYNAMIC_VAR
3109# define DYNAMIC_VAR
3110# endif
3111#endif
3112
Eric Andersenc470f442003-07-28 09:56:35 +00003113
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003114/* jobs.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003115
Eric Andersenc470f442003-07-28 09:56:35 +00003116/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3117#define FORK_FG 0
3118#define FORK_BG 1
3119#define FORK_NOJOB 2
3120
3121/* mode flags for showjob(s) */
3122#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3123#define SHOW_PID 0x04 /* include process pid */
3124#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3125
3126
3127/*
3128 * A job structure contains information about a job. A job is either a
3129 * single process or a set of processes contained in a pipeline. In the
3130 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3131 * array of pids.
3132 */
3133
3134struct procstat {
3135 pid_t pid; /* process id */
3136 int status; /* last process status from wait() */
3137 char *cmd; /* text of command being run */
3138};
3139
3140struct job {
3141 struct procstat ps0; /* status of process */
3142 struct procstat *ps; /* status or processes when more than one */
3143#if JOBS
3144 int stopstatus; /* status of a stopped job */
3145#endif
3146 uint32_t
3147 nprocs: 16, /* number of processes */
3148 state: 8,
3149#define JOBRUNNING 0 /* at least one proc running */
3150#define JOBSTOPPED 1 /* all procs are stopped */
3151#define JOBDONE 2 /* all procs are completed */
3152#if JOBS
3153 sigint: 1, /* job was killed by SIGINT */
3154 jobctl: 1, /* job running under job control */
3155#endif
3156 waited: 1, /* true if this entry has been waited for */
3157 used: 1, /* true if this entry is in used */
3158 changed: 1; /* true if status has changed */
3159 struct job *prev_job; /* previous job */
3160};
3161
3162static pid_t backgndpid; /* pid of last background process */
3163static int job_warning; /* user was warned about stopped jobs */
3164#if JOBS
3165static int jobctl; /* true if doing job control */
3166#endif
3167
3168static struct job *makejob(union node *, int);
3169static int forkshell(struct job *, union node *, int);
3170static int waitforjob(struct job *);
Eric Andersenc470f442003-07-28 09:56:35 +00003171
3172#if ! JOBS
3173#define setjobctl(on) /* do nothing */
3174#else
3175static void setjobctl(int);
3176static void showjobs(FILE *, int);
3177#endif
3178
Eric Andersenc470f442003-07-28 09:56:35 +00003179
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003180/* main.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003181
Eric Andersenc470f442003-07-28 09:56:35 +00003182static void readcmdfile(char *);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00003183
Eric Andersenc470f442003-07-28 09:56:35 +00003184
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003185/* options.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003186
Eric Andersenc470f442003-07-28 09:56:35 +00003187static char *minusc; /* argument to -c option */
3188
Eric Andersenc470f442003-07-28 09:56:35 +00003189static void optschanged(void);
3190static void setparam(char **);
3191static void freeparam(volatile struct shparam *);
3192static int shiftcmd(int, char **);
3193static int setcmd(int, char **);
3194static int nextopt(const char *);
3195
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003196/* redir.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003197
3198/* flags passed to redirect */
3199#define REDIR_PUSH 01 /* save previous values of file descriptors */
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003200#define REDIR_SAVEFD2 03 /* set preverrout */
Eric Andersenc470f442003-07-28 09:56:35 +00003201
Eric Andersenc470f442003-07-28 09:56:35 +00003202static void redirect(union node *, int);
3203static void popredir(int);
3204static void clearredir(int);
3205static int copyfd(int, int);
3206static int redirectsafe(union node *, int);
3207
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003208/* trap.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003209
Eric Andersenc470f442003-07-28 09:56:35 +00003210static void clear_traps(void);
3211static void setsignal(int);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003212static int dotrap(void);
Eric Andersenc470f442003-07-28 09:56:35 +00003213static void setinteractive(int);
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +00003214static void exitshell(void) ATTRIBUTE_NORETURN;
Eric Andersenc470f442003-07-28 09:56:35 +00003215
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00003216
3217static int is_safe_applet(char *name)
3218{
Denis Vlasenko8f27c342006-12-26 21:31:11 +00003219 /* It isn't a bug to have non-existent applet here... */
3220 /* ...just a waste of space... */
Denis Vlasenkof7996f32007-01-11 17:20:00 +00003221 static const char safe_applets[][8] = {
Denis Vlasenko8f27c342006-12-26 21:31:11 +00003222 "["
3223 USE_AWK (, "awk" )
3224 USE_CAT (, "cat" )
3225 USE_CHMOD (, "chmod" )
3226 USE_CHOWN (, "chown" )
3227 USE_CP (, "cp" )
3228 USE_CUT (, "cut" )
3229 USE_DD (, "dd" )
3230 USE_ECHO (, "echo" )
3231 USE_FIND (, "find" )
3232 USE_HEXDUMP(, "hexdump")
3233 USE_LN (, "ln" )
3234 USE_LS (, "ls" )
3235 USE_MKDIR (, "mkdir" )
3236 USE_RM (, "rm" )
3237 USE_SORT (, "sort" )
3238 USE_TEST (, "test" )
3239 USE_TOUCH (, "touch" )
3240 USE_XARGS (, "xargs" )
3241 };
3242 int n = sizeof(safe_applets) / sizeof(safe_applets[0]);
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00003243 int i;
3244 for (i = 0; i < n; i++)
3245 if (strcmp(safe_applets[i], name) == 0)
3246 return 1;
3247
3248 return 0;
3249}
3250
3251
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003252/* ============ Alias handling */
Denis Vlasenko131ae172007-02-18 13:00:19 +00003253#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003254
3255#define ALIASINUSE 1
3256#define ALIASDEAD 2
3257
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003258struct alias {
3259 struct alias *next;
3260 char *name;
3261 char *val;
3262 int flag;
3263};
3264
Eric Andersen2870d962001-07-02 17:27:21 +00003265static struct alias *atab[ATABSIZE];
3266
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003267static struct alias **
3268__lookupalias(const char *name) {
3269 unsigned int hashval;
3270 struct alias **app;
3271 const char *p;
3272 unsigned int ch;
3273
3274 p = name;
3275
3276 ch = (unsigned char)*p;
3277 hashval = ch << 4;
3278 while (ch) {
3279 hashval += ch;
3280 ch = (unsigned char)*++p;
3281 }
3282 app = &atab[hashval % ATABSIZE];
3283
3284 for (; *app; app = &(*app)->next) {
3285 if (strcmp(name, (*app)->name) == 0) {
3286 break;
3287 }
3288 }
3289
3290 return app;
3291}
3292
3293static struct alias *
3294lookupalias(const char *name, int check)
3295{
3296 struct alias *ap = *__lookupalias(name);
3297
3298 if (check && ap && (ap->flag & ALIASINUSE))
3299 return NULL;
3300 return ap;
3301}
3302
3303static struct alias *
3304freealias(struct alias *ap)
3305{
3306 struct alias *next;
3307
3308 if (ap->flag & ALIASINUSE) {
3309 ap->flag |= ALIASDEAD;
3310 return ap;
3311 }
3312
3313 next = ap->next;
3314 free(ap->name);
3315 free(ap->val);
3316 free(ap);
3317 return next;
3318}
Eric Andersencb57d552001-06-28 07:25:16 +00003319
Eric Andersenc470f442003-07-28 09:56:35 +00003320static void
3321setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003322{
3323 struct alias *ap, **app;
3324
3325 app = __lookupalias(name);
3326 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003327 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003328 if (ap) {
3329 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003330 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003331 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003332 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003333 ap->flag &= ~ALIASDEAD;
3334 } else {
3335 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003336 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003337 ap->name = ckstrdup(name);
3338 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003339 ap->flag = 0;
3340 ap->next = 0;
3341 *app = ap;
3342 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003343 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003344}
3345
Eric Andersenc470f442003-07-28 09:56:35 +00003346static int
3347unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003348{
Eric Andersencb57d552001-06-28 07:25:16 +00003349 struct alias **app;
3350
3351 app = __lookupalias(name);
3352
3353 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003354 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003355 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003356 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003357 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003358 }
3359
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003360 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003361}
3362
Eric Andersenc470f442003-07-28 09:56:35 +00003363static void
3364rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003365{
Eric Andersencb57d552001-06-28 07:25:16 +00003366 struct alias *ap, **app;
3367 int i;
3368
Denis Vlasenkob012b102007-02-19 22:43:01 +00003369 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003370 for (i = 0; i < ATABSIZE; i++) {
3371 app = &atab[i];
3372 for (ap = *app; ap; ap = *app) {
3373 *app = freealias(*app);
3374 if (ap == *app) {
3375 app = &ap->next;
3376 }
3377 }
3378 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003379 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003380}
3381
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003382static void
3383printalias(const struct alias *ap)
3384{
3385 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3386}
3387
Eric Andersencb57d552001-06-28 07:25:16 +00003388/*
3389 * TODO - sort output
3390 */
Eric Andersenc470f442003-07-28 09:56:35 +00003391static int
3392aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003393{
3394 char *n, *v;
3395 int ret = 0;
3396 struct alias *ap;
3397
3398 if (argc == 1) {
3399 int i;
3400
3401 for (i = 0; i < ATABSIZE; i++)
3402 for (ap = atab[i]; ap; ap = ap->next) {
3403 printalias(ap);
3404 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003405 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003406 }
3407 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003408 v = strchr(n+1, '=');
3409 if (v == NULL) { /* n+1: funny ksh stuff */
3410 ap = *__lookupalias(n);
3411 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003412 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003413 ret = 1;
3414 } else
3415 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003416 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003417 *v++ = '\0';
3418 setalias(n, v);
3419 }
3420 }
3421
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003422 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003423}
3424
Eric Andersenc470f442003-07-28 09:56:35 +00003425static int
3426unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003427{
3428 int i;
3429
3430 while ((i = nextopt("a")) != '\0') {
3431 if (i == 'a') {
3432 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003433 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003434 }
3435 }
3436 for (i = 0; *argptr; argptr++) {
3437 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003438 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003439 i = 1;
3440 }
3441 }
3442
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003443 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003444}
Denis Vlasenko131ae172007-02-18 13:00:19 +00003445#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003446
Eric Andersenc470f442003-07-28 09:56:35 +00003447
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003448/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00003449
3450/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00003451#define EV_EXIT 01 /* exit after evaluating tree */
3452#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
3453#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00003454
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003455/* forward declarations - evaluation is faily recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00003456static void evalloop(union node *, int);
3457static void evalfor(union node *, int);
3458static void evalcase(union node *, int);
3459static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003460static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00003461static void evalpipe(union node *, int);
3462static void evalcommand(union node *, int);
3463static int evalbltin(const struct builtincmd *, int, char **);
3464static int evalfun(struct funcnode *, int, char **, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003465static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003466
Eric Andersen62483552001-07-10 06:09:16 +00003467/*
Eric Andersenc470f442003-07-28 09:56:35 +00003468 * Evaluate a parse tree. The value is left in the global variable
3469 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00003470 */
Eric Andersenc470f442003-07-28 09:56:35 +00003471static void
3472evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00003473{
Eric Andersenc470f442003-07-28 09:56:35 +00003474 int checkexit = 0;
3475 void (*evalfn)(union node *, int);
3476 unsigned isor;
3477 int status;
3478 if (n == NULL) {
3479 TRACE(("evaltree(NULL) called\n"));
3480 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00003481 }
Eric Andersenc470f442003-07-28 09:56:35 +00003482 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003483 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00003484 switch (n->type) {
3485 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00003486#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00003487 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003488 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00003489 break;
3490#endif
3491 case NNOT:
3492 evaltree(n->nnot.com, EV_TESTED);
3493 status = !exitstatus;
3494 goto setstatus;
3495 case NREDIR:
3496 expredir(n->nredir.redirect);
3497 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
3498 if (!status) {
3499 evaltree(n->nredir.n, flags & EV_TESTED);
3500 status = exitstatus;
3501 }
3502 popredir(0);
3503 goto setstatus;
3504 case NCMD:
3505 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003506 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00003507 if (eflag && !(flags & EV_TESTED))
3508 checkexit = ~0;
3509 goto calleval;
3510 case NFOR:
3511 evalfn = evalfor;
3512 goto calleval;
3513 case NWHILE:
3514 case NUNTIL:
3515 evalfn = evalloop;
3516 goto calleval;
3517 case NSUBSHELL:
3518 case NBACKGND:
3519 evalfn = evalsubshell;
3520 goto calleval;
3521 case NPIPE:
3522 evalfn = evalpipe;
3523 goto checkexit;
3524 case NCASE:
3525 evalfn = evalcase;
3526 goto calleval;
3527 case NAND:
3528 case NOR:
3529 case NSEMI:
3530#if NAND + 1 != NOR
3531#error NAND + 1 != NOR
3532#endif
3533#if NOR + 1 != NSEMI
3534#error NOR + 1 != NSEMI
3535#endif
3536 isor = n->type - NAND;
3537 evaltree(
3538 n->nbinary.ch1,
3539 (flags | ((isor >> 1) - 1)) & EV_TESTED
3540 );
3541 if (!exitstatus == isor)
3542 break;
3543 if (!evalskip) {
3544 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003545 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00003546 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003547 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00003548 evalfn(n, flags);
3549 break;
3550 }
3551 break;
3552 case NIF:
3553 evaltree(n->nif.test, EV_TESTED);
3554 if (evalskip)
3555 break;
3556 if (exitstatus == 0) {
3557 n = n->nif.ifpart;
3558 goto evaln;
3559 } else if (n->nif.elsepart) {
3560 n = n->nif.elsepart;
3561 goto evaln;
3562 }
3563 goto success;
3564 case NDEFUN:
3565 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003566 success:
Eric Andersenc470f442003-07-28 09:56:35 +00003567 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003568 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00003569 exitstatus = status;
3570 break;
3571 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003572 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003573 if ((checkexit & exitstatus))
3574 evalskip |= SKIPEVAL;
3575 else if (pendingsigs && dotrap())
3576 goto exexit;
3577
3578 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003579 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00003580 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003581 }
Eric Andersen62483552001-07-10 06:09:16 +00003582}
3583
Eric Andersenc470f442003-07-28 09:56:35 +00003584#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
3585static
3586#endif
3587void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
3588
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003589static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00003590
3591static void
3592evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003593{
3594 int status;
3595
3596 loopnest++;
3597 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003598 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00003599 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00003600 int i;
3601
Eric Andersencb57d552001-06-28 07:25:16 +00003602 evaltree(n->nbinary.ch1, EV_TESTED);
3603 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003604 skipping:
3605 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003606 evalskip = 0;
3607 continue;
3608 }
3609 if (evalskip == SKIPBREAK && --skipcount <= 0)
3610 evalskip = 0;
3611 break;
3612 }
Eric Andersenc470f442003-07-28 09:56:35 +00003613 i = exitstatus;
3614 if (n->type != NWHILE)
3615 i = !i;
3616 if (i != 0)
3617 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003618 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00003619 status = exitstatus;
3620 if (evalskip)
3621 goto skipping;
3622 }
3623 loopnest--;
3624 exitstatus = status;
3625}
3626
Eric Andersenc470f442003-07-28 09:56:35 +00003627static void
3628evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003629{
3630 struct arglist arglist;
3631 union node *argp;
3632 struct strlist *sp;
3633 struct stackmark smark;
3634
3635 setstackmark(&smark);
3636 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003637 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003638 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00003639 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00003640 if (evalskip)
3641 goto out;
3642 }
3643 *arglist.lastp = NULL;
3644
3645 exitstatus = 0;
3646 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003647 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003648 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003649 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003650 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00003651 if (evalskip) {
3652 if (evalskip == SKIPCONT && --skipcount <= 0) {
3653 evalskip = 0;
3654 continue;
3655 }
3656 if (evalskip == SKIPBREAK && --skipcount <= 0)
3657 evalskip = 0;
3658 break;
3659 }
3660 }
3661 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003662 out:
Eric Andersencb57d552001-06-28 07:25:16 +00003663 popstackmark(&smark);
3664}
3665
Eric Andersenc470f442003-07-28 09:56:35 +00003666static void
3667evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003668{
3669 union node *cp;
3670 union node *patp;
3671 struct arglist arglist;
3672 struct stackmark smark;
3673
3674 setstackmark(&smark);
3675 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00003676 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00003677 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003678 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
3679 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003680 if (casematch(patp, arglist.list->text)) {
3681 if (evalskip == 0) {
3682 evaltree(cp->nclist.body, flags);
3683 }
3684 goto out;
3685 }
3686 }
3687 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003688 out:
Eric Andersencb57d552001-06-28 07:25:16 +00003689 popstackmark(&smark);
3690}
3691
Eric Andersenc470f442003-07-28 09:56:35 +00003692/*
3693 * Kick off a subshell to evaluate a tree.
3694 */
Eric Andersenc470f442003-07-28 09:56:35 +00003695static void
3696evalsubshell(union node *n, int flags)
3697{
3698 struct job *jp;
3699 int backgnd = (n->type == NBACKGND);
3700 int status;
3701
3702 expredir(n->nredir.redirect);
3703 if (!backgnd && flags & EV_EXIT && !trap[0])
3704 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003705 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00003706 jp = makejob(n, 1);
3707 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003708 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00003709 flags |= EV_EXIT;
3710 if (backgnd)
3711 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003712 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00003713 redirect(n->nredir.redirect, 0);
3714 evaltreenr(n->nredir.n, flags);
3715 /* never returns */
3716 }
3717 status = 0;
3718 if (! backgnd)
3719 status = waitforjob(jp);
3720 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003721 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00003722}
3723
Eric Andersenc470f442003-07-28 09:56:35 +00003724/*
3725 * Compute the names of the files in a redirection list.
3726 */
Eric Andersenc470f442003-07-28 09:56:35 +00003727static void
3728expredir(union node *n)
3729{
3730 union node *redir;
3731
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003732 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00003733 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003734
3735 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00003736 fn.lastp = &fn.list;
3737 switch (redir->type) {
3738 case NFROMTO:
3739 case NFROM:
3740 case NTO:
3741 case NCLOBBER:
3742 case NAPPEND:
3743 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
3744 redir->nfile.expfname = fn.list->text;
3745 break;
3746 case NFROMFD:
3747 case NTOFD:
3748 if (redir->ndup.vname) {
3749 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003750 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00003751 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003752 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00003753 }
3754 break;
3755 }
3756 }
3757}
3758
Eric Andersencb57d552001-06-28 07:25:16 +00003759/*
Eric Andersencb57d552001-06-28 07:25:16 +00003760 * Evaluate a pipeline. All the processes in the pipeline are children
3761 * of the process creating the pipeline. (This differs from some versions
3762 * of the shell, which make the last process in a pipeline the parent
3763 * of all the rest.)
3764 */
Eric Andersenc470f442003-07-28 09:56:35 +00003765static void
3766evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003767{
3768 struct job *jp;
3769 struct nodelist *lp;
3770 int pipelen;
3771 int prevfd;
3772 int pip[2];
3773
Eric Andersenc470f442003-07-28 09:56:35 +00003774 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00003775 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003776 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00003777 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003778 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003779 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003780 jp = makejob(n, pipelen);
3781 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003782 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003783 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00003784 pip[1] = -1;
3785 if (lp->next) {
3786 if (pipe(pip) < 0) {
3787 close(prevfd);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003788 ash_msg_and_raise_error("Pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00003789 }
3790 }
3791 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003792 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003793 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003794 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00003795 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003796 if (prevfd > 0) {
3797 dup2(prevfd, 0);
3798 close(prevfd);
3799 }
3800 if (pip[1] > 1) {
3801 dup2(pip[1], 1);
3802 close(pip[1]);
3803 }
Eric Andersenc470f442003-07-28 09:56:35 +00003804 evaltreenr(lp->n, flags);
3805 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00003806 }
3807 if (prevfd >= 0)
3808 close(prevfd);
3809 prevfd = pip[0];
3810 close(pip[1]);
3811 }
Eric Andersencb57d552001-06-28 07:25:16 +00003812 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003813 exitstatus = waitforjob(jp);
3814 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00003815 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003816 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003817}
3818
Denis Vlasenko131ae172007-02-18 13:00:19 +00003819#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00003820static char **
3821parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00003822{
3823 char *cp, c;
3824
3825 for (;;) {
3826 cp = *++argv;
3827 if (!cp)
3828 return 0;
3829 if (*cp++ != '-')
3830 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003831 c = *cp++;
3832 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00003833 break;
3834 if (c == '-' && !*cp) {
3835 argv++;
3836 break;
3837 }
3838 do {
3839 switch (c) {
3840 case 'p':
3841 *path = defpath;
3842 break;
3843 default:
3844 /* run 'typecmd' for other options */
3845 return 0;
3846 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00003847 c = *cp++;
3848 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00003849 }
3850 return argv;
3851}
3852#endif
3853
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003854/* Forward declarations for builtintab[] */
3855#if JOBS
3856static int fg_bgcmd(int, char **);
3857#endif
3858static int breakcmd(int, char **);
3859#if ENABLE_ASH_CMDCMD
3860static int commandcmd(int, char **);
3861#endif
3862static int dotcmd(int, char **);
3863static int evalcmd(int, char **);
3864#if ENABLE_ASH_BUILTIN_ECHO
3865static int echocmd(int, char **);
3866#endif
3867#if ENABLE_ASH_BUILTIN_TEST
3868static int testcmd(int, char **);
3869#endif
3870static int execcmd(int, char **);
3871static int exitcmd(int, char **);
3872static int exportcmd(int, char **);
3873static int falsecmd(int, char **);
3874#if ENABLE_ASH_GETOPTS
3875static int getoptscmd(int, char **);
3876#endif
3877static int hashcmd(int, char **);
3878#if !ENABLE_FEATURE_SH_EXTRA_QUIET
3879static int helpcmd(int argc, char **argv);
3880#endif
3881#if JOBS
3882static int jobscmd(int, char **);
3883#endif
3884#if ENABLE_ASH_MATH_SUPPORT
3885static int letcmd(int, char **);
3886#endif
3887static int localcmd(int, char **);
3888static int pwdcmd(int, char **);
3889static int readcmd(int, char **);
3890static int returncmd(int, char **);
3891static int setcmd(int, char **);
3892static int shiftcmd(int, char **);
3893static int timescmd(int, char **);
3894static int trapcmd(int, char **);
3895static int truecmd(int, char **);
3896static int typecmd(int, char **);
3897static int umaskcmd(int, char **);
3898static int unsetcmd(int, char **);
3899static int waitcmd(int, char **);
3900static int ulimitcmd(int, char **);
3901#if JOBS
3902static int killcmd(int, char **);
3903#endif
3904
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00003905#define BUILTIN_NOSPEC "0"
3906#define BUILTIN_SPECIAL "1"
3907#define BUILTIN_REGULAR "2"
3908#define BUILTIN_SPEC_REG "3"
3909#define BUILTIN_ASSIGN "4"
3910#define BUILTIN_SPEC_ASSG "5"
3911#define BUILTIN_REG_ASSG "6"
3912#define BUILTIN_SPEC_REG_ASSG "7"
3913
3914#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
3915#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
3916#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
3917
3918/* make sure to keep these in proper order since it is searched via bsearch() */
3919static const struct builtincmd builtintab[] = {
3920 { BUILTIN_SPEC_REG ".", dotcmd },
3921 { BUILTIN_SPEC_REG ":", truecmd },
3922#if ENABLE_ASH_BUILTIN_TEST
3923 { BUILTIN_REGULAR "[", testcmd },
3924 { BUILTIN_REGULAR "[[", testcmd },
3925#endif
3926#if ENABLE_ASH_ALIAS
3927 { BUILTIN_REG_ASSG "alias", aliascmd },
3928#endif
3929#if JOBS
3930 { BUILTIN_REGULAR "bg", fg_bgcmd },
3931#endif
3932 { BUILTIN_SPEC_REG "break", breakcmd },
3933 { BUILTIN_REGULAR "cd", cdcmd },
3934 { BUILTIN_NOSPEC "chdir", cdcmd },
3935#if ENABLE_ASH_CMDCMD
3936 { BUILTIN_REGULAR "command", commandcmd },
3937#endif
3938 { BUILTIN_SPEC_REG "continue", breakcmd },
3939#if ENABLE_ASH_BUILTIN_ECHO
3940 { BUILTIN_REGULAR "echo", echocmd },
3941#endif
3942 { BUILTIN_SPEC_REG "eval", evalcmd },
3943 { BUILTIN_SPEC_REG "exec", execcmd },
3944 { BUILTIN_SPEC_REG "exit", exitcmd },
3945 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
3946 { BUILTIN_REGULAR "false", falsecmd },
3947#if JOBS
3948 { BUILTIN_REGULAR "fg", fg_bgcmd },
3949#endif
3950#if ENABLE_ASH_GETOPTS
3951 { BUILTIN_REGULAR "getopts", getoptscmd },
3952#endif
3953 { BUILTIN_NOSPEC "hash", hashcmd },
3954#if !ENABLE_FEATURE_SH_EXTRA_QUIET
3955 { BUILTIN_NOSPEC "help", helpcmd },
3956#endif
3957#if JOBS
3958 { BUILTIN_REGULAR "jobs", jobscmd },
3959 { BUILTIN_REGULAR "kill", killcmd },
3960#endif
3961#if ENABLE_ASH_MATH_SUPPORT
3962 { BUILTIN_NOSPEC "let", letcmd },
3963#endif
3964 { BUILTIN_ASSIGN "local", localcmd },
3965 { BUILTIN_NOSPEC "pwd", pwdcmd },
3966 { BUILTIN_REGULAR "read", readcmd },
3967 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
3968 { BUILTIN_SPEC_REG "return", returncmd },
3969 { BUILTIN_SPEC_REG "set", setcmd },
3970 { BUILTIN_SPEC_REG "shift", shiftcmd },
3971 { BUILTIN_SPEC_REG "source", dotcmd },
3972#if ENABLE_ASH_BUILTIN_TEST
3973 { BUILTIN_REGULAR "test", testcmd },
3974#endif
3975 { BUILTIN_SPEC_REG "times", timescmd },
3976 { BUILTIN_SPEC_REG "trap", trapcmd },
3977 { BUILTIN_REGULAR "true", truecmd },
3978 { BUILTIN_NOSPEC "type", typecmd },
3979 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
3980 { BUILTIN_REGULAR "umask", umaskcmd },
3981#if ENABLE_ASH_ALIAS
3982 { BUILTIN_REGULAR "unalias", unaliascmd },
3983#endif
3984 { BUILTIN_SPEC_REG "unset", unsetcmd },
3985 { BUILTIN_REGULAR "wait", waitcmd },
3986};
3987
3988#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
3989
3990#define COMMANDCMD (builtintab + 5 + \
3991 2 * ENABLE_ASH_BUILTIN_TEST + \
3992 ENABLE_ASH_ALIAS + \
3993 ENABLE_ASH_JOB_CONTROL)
3994#define EXECCMD (builtintab + 7 + \
3995 2 * ENABLE_ASH_BUILTIN_TEST + \
3996 ENABLE_ASH_ALIAS + \
3997 ENABLE_ASH_JOB_CONTROL + \
3998 ENABLE_ASH_CMDCMD + \
3999 ENABLE_ASH_BUILTIN_ECHO)
4000
4001/*
4002 * Execute a simple command.
4003 */
4004static int back_exitstatus; /* exit status of backquoted command */
4005static int
4006isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00004007{
4008 const char *q = endofname(p);
4009 if (p == q)
4010 return 0;
4011 return *q == '=';
4012}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00004013static int
4014bltincmd(int argc, char **argv)
4015{
4016 /* Preserve exitstatus of a previous possible redirection
4017 * as POSIX mandates */
4018 return back_exitstatus;
4019}
Eric Andersenc470f442003-07-28 09:56:35 +00004020static void
4021evalcommand(union node *cmd, int flags)
4022{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00004023 static const struct builtincmd bltin = {
4024 "\0\0", bltincmd
4025 };
Eric Andersenc470f442003-07-28 09:56:35 +00004026 struct stackmark smark;
4027 union node *argp;
4028 struct arglist arglist;
4029 struct arglist varlist;
4030 char **argv;
4031 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004032 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00004033 struct cmdentry cmdentry;
4034 struct job *jp;
4035 char *lastarg;
4036 const char *path;
4037 int spclbltin;
4038 int cmd_is_exec;
4039 int status;
4040 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00004041 struct builtincmd *bcmd;
4042 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00004043
4044 /* First expand the arguments. */
4045 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
4046 setstackmark(&smark);
4047 back_exitstatus = 0;
4048
4049 cmdentry.cmdtype = CMDBUILTIN;
4050 cmdentry.u.cmd = &bltin;
4051 varlist.lastp = &varlist.list;
4052 *varlist.lastp = NULL;
4053 arglist.lastp = &arglist.list;
4054 *arglist.lastp = NULL;
4055
4056 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004057 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00004058 bcmd = find_builtin(cmd->ncmd.args->narg.text);
4059 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
4060 }
4061
Eric Andersenc470f442003-07-28 09:56:35 +00004062 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
4063 struct strlist **spp;
4064
4065 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00004066 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00004067 expandarg(argp, &arglist, EXP_VARTILDE);
4068 else
4069 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
4070
Eric Andersenc470f442003-07-28 09:56:35 +00004071 for (sp = *spp; sp; sp = sp->next)
4072 argc++;
4073 }
4074
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004075 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004076 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00004077 TRACE(("evalcommand arg: %s\n", sp->text));
4078 *nargv++ = sp->text;
4079 }
4080 *nargv = NULL;
4081
4082 lastarg = NULL;
4083 if (iflag && funcnest == 0 && argc > 0)
4084 lastarg = nargv[-1];
4085
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004086 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00004087 expredir(cmd->ncmd.redirect);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004088 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00004089
4090 path = vpath.text;
4091 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
4092 struct strlist **spp;
4093 char *p;
4094
4095 spp = varlist.lastp;
4096 expandarg(argp, &varlist, EXP_VARTILDE);
4097
4098 /*
4099 * Modify the command lookup path, if a PATH= assignment
4100 * is present
4101 */
4102 p = (*spp)->text;
4103 if (varequal(p, path))
4104 path = p;
4105 }
4106
4107 /* Print the command if xflag is set. */
4108 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004109 int n;
4110 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00004111
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004112 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00004113 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004114
4115 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004116 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004117 while (sp) {
4118 dprintf(preverrout_fd, p, sp->text);
4119 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004120 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004121 p--;
4122 }
4123 }
4124 sp = arglist.list;
4125 }
Rob Landley53437472006-07-16 08:14:35 +00004126 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00004127 }
4128
4129 cmd_is_exec = 0;
4130 spclbltin = -1;
4131
4132 /* Now locate the command. */
4133 if (argc) {
4134 const char *oldpath;
4135 int cmd_flag = DO_ERR;
4136
4137 path += 5;
4138 oldpath = path;
4139 for (;;) {
4140 find_command(argv[0], &cmdentry, cmd_flag, path);
4141 if (cmdentry.cmdtype == CMDUNKNOWN) {
4142 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004143 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00004144 goto bail;
4145 }
4146
4147 /* implement bltin and command here */
4148 if (cmdentry.cmdtype != CMDBUILTIN)
4149 break;
4150 if (spclbltin < 0)
4151 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
4152 if (cmdentry.u.cmd == EXECCMD)
4153 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00004154#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00004155 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00004156 path = oldpath;
4157 nargv = parse_command_args(argv, &path);
4158 if (!nargv)
4159 break;
4160 argc -= nargv - argv;
4161 argv = nargv;
4162 cmd_flag |= DO_NOFUNC;
4163 } else
4164#endif
4165 break;
4166 }
4167 }
4168
4169 if (status) {
4170 /* We have a redirection error. */
4171 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00004172 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004173 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00004174 exitstatus = status;
4175 goto out;
4176 }
4177
4178 /* Execute the command. */
4179 switch (cmdentry.cmdtype) {
4180 default:
4181 /* Fork off a child process if necessary. */
4182 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00004183 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00004184 jp = makejob(cmd, 1);
4185 if (forkshell(jp, cmd, FORK_FG) != 0) {
4186 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00004187 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00004188 break;
4189 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004190 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00004191 }
4192 listsetvar(varlist.list, VEXPORT|VSTACK);
4193 shellexec(argv, path, cmdentry.u.index);
4194 /* NOTREACHED */
4195
4196 case CMDBUILTIN:
4197 cmdenviron = varlist.list;
4198 if (cmdenviron) {
4199 struct strlist *list = cmdenviron;
4200 int i = VNOSET;
4201 if (spclbltin > 0 || argc == 0) {
4202 i = 0;
4203 if (cmd_is_exec && argc > 1)
4204 i = VEXPORT;
4205 }
4206 listsetvar(list, i);
4207 }
4208 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
4209 int exit_status;
4210 int i, j;
4211
4212 i = exception;
4213 if (i == EXEXIT)
4214 goto raise;
4215
4216 exit_status = 2;
4217 j = 0;
4218 if (i == EXINT)
4219 j = SIGINT;
4220 if (i == EXSIG)
4221 j = pendingsigs;
4222 if (j)
4223 exit_status = j + 128;
4224 exitstatus = exit_status;
4225
4226 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004227 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004228 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00004229 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004230 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00004231 }
4232 break;
4233
4234 case CMDFUNCTION:
4235 listsetvar(varlist.list, 0);
4236 if (evalfun(cmdentry.u.func, argc, argv, flags))
4237 goto raise;
4238 break;
4239 }
4240
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004241 out:
Eric Andersenc470f442003-07-28 09:56:35 +00004242 popredir(cmd_is_exec);
4243 if (lastarg)
4244 /* dsl: I think this is intended to be used to support
4245 * '_' in 'vi' command mode during line editing...
4246 * However I implemented that within libedit itself.
4247 */
4248 setvar("_", lastarg, 0);
4249 popstackmark(&smark);
4250}
4251
4252static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004253evalbltin(const struct builtincmd *cmd, int argc, char **argv)
4254{
Eric Andersenc470f442003-07-28 09:56:35 +00004255 char *volatile savecmdname;
4256 struct jmploc *volatile savehandler;
4257 struct jmploc jmploc;
4258 int i;
4259
4260 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004261 i = setjmp(jmploc.loc);
4262 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00004263 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004264 savehandler = exception_handler;
4265 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00004266 commandname = argv[0];
4267 argptr = argv + 1;
4268 optptr = NULL; /* initialize nextopt */
4269 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00004270 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004271 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004272 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00004273 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00004274 commandname = savecmdname;
4275 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004276 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00004277
4278 return i;
4279}
4280
Denis Vlasenkoaa744452007-02-23 01:04:22 +00004281static struct localvar *localvars;
4282
4283/*
4284 * Called after a function returns.
4285 * Interrupts must be off.
4286 */
4287static void
4288poplocalvars(void)
4289{
4290 struct localvar *lvp;
4291 struct var *vp;
4292
4293 while ((lvp = localvars) != NULL) {
4294 localvars = lvp->next;
4295 vp = lvp->vp;
4296 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
4297 if (vp == NULL) { /* $- saved */
4298 memcpy(optlist, lvp->text, sizeof(optlist));
4299 free((char*)lvp->text);
4300 optschanged();
4301 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
4302 unsetvar(vp->text);
4303 } else {
4304 if (vp->func)
4305 (*vp->func)(strchrnul(lvp->text, '=') + 1);
4306 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
4307 free((char*)vp->text);
4308 vp->flags = lvp->flags;
4309 vp->text = lvp->text;
4310 }
4311 free(lvp);
4312 }
4313}
4314
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00004315/*
4316 * Free a parse tree.
4317 */
4318static void
4319freefunc(struct funcnode *f)
4320{
4321 if (f && --f->count < 0)
4322 free(f);
4323}
4324
Eric Andersenc470f442003-07-28 09:56:35 +00004325static int
4326evalfun(struct funcnode *func, int argc, char **argv, int flags)
4327{
4328 volatile struct shparam saveparam;
4329 struct localvar *volatile savelocalvars;
4330 struct jmploc *volatile savehandler;
4331 struct jmploc jmploc;
4332 int e;
4333
4334 saveparam = shellparam;
4335 savelocalvars = localvars;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004336 e = setjmp(jmploc.loc);
4337 if (e) {
Eric Andersenc470f442003-07-28 09:56:35 +00004338 goto funcdone;
4339 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004340 INT_OFF;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004341 savehandler = exception_handler;
4342 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00004343 localvars = NULL;
4344 shellparam.malloc = 0;
4345 func->count++;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004346 funcnest++;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004347 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00004348 shellparam.nparam = argc - 1;
4349 shellparam.p = argv + 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00004350#if ENABLE_ASH_GETOPTS
Eric Andersenc470f442003-07-28 09:56:35 +00004351 shellparam.optind = 1;
4352 shellparam.optoff = -1;
4353#endif
Eric Andersenc470f442003-07-28 09:56:35 +00004354 evaltree(&func->n, flags & EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00004355funcdone:
Denis Vlasenkob012b102007-02-19 22:43:01 +00004356 INT_OFF;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004357 funcnest--;
Eric Andersenc470f442003-07-28 09:56:35 +00004358 freefunc(func);
4359 poplocalvars();
4360 localvars = savelocalvars;
4361 freeparam(&shellparam);
4362 shellparam = saveparam;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004363 exception_handler = savehandler;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004364 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004365 evalskip &= ~SKIPFUNC;
Eric Andersenc470f442003-07-28 09:56:35 +00004366 return e;
4367}
4368
4369
Denis Vlasenkoaa744452007-02-23 01:04:22 +00004370static int
4371goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00004372{
4373 return !*endofname(p);
4374}
4375
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004376
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004377/*
4378 * Search for a command. This is called before we fork so that the
4379 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00004380 * the child. The check for "goodname" is an overly conservative
4381 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004382 */
Eric Andersenc470f442003-07-28 09:56:35 +00004383static void
4384prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004385{
4386 struct cmdentry entry;
4387
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004388 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
4389 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004390}
4391
Eric Andersencb57d552001-06-28 07:25:16 +00004392
Eric Andersencb57d552001-06-28 07:25:16 +00004393/*
4394 * Builtin commands. Builtin commands whose functions are closely
4395 * tied to evaluation are implemented here.
4396 */
4397
4398/*
Eric Andersencb57d552001-06-28 07:25:16 +00004399 * Handle break and continue commands. Break, continue, and return are
4400 * all handled by setting the evalskip flag. The evaluation routines
4401 * above all check this flag, and if it is set they start skipping
4402 * commands rather than executing them. The variable skipcount is
4403 * the number of loops to break/continue, or the number of function
4404 * levels to return. (The latter is always 1.) It should probably
4405 * be an error to break out of more loops than exist, but it isn't
4406 * in the standard shell so we don't make it one here.
4407 */
4408
Eric Andersenc470f442003-07-28 09:56:35 +00004409static int
4410breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004411{
4412 int n = argc > 1 ? number(argv[1]) : 1;
4413
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00004414 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00004415 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00004416 if (n > loopnest)
4417 n = loopnest;
4418 if (n > 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00004419 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00004420 skipcount = n;
4421 }
4422 return 0;
4423}
4424
Eric Andersencb57d552001-06-28 07:25:16 +00004425/*
4426 * The return command.
4427 */
Eric Andersenc470f442003-07-28 09:56:35 +00004428static int
4429returncmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004430{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004431 /*
4432 * If called outside a function, do what ksh does;
4433 * skip the rest of the file.
4434 */
4435 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
4436 return argv[1] ? number(argv[1]) : exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00004437}
4438
Eric Andersenc470f442003-07-28 09:56:35 +00004439static int
4440falsecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004441{
4442 return 1;
4443}
4444
Eric Andersenc470f442003-07-28 09:56:35 +00004445static int
4446truecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004447{
4448 return 0;
4449}
Eric Andersen2870d962001-07-02 17:27:21 +00004450
Eric Andersenc470f442003-07-28 09:56:35 +00004451static int
4452execcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004453{
4454 if (argc > 1) {
Eric Andersenc470f442003-07-28 09:56:35 +00004455 iflag = 0; /* exit on error */
Eric Andersencb57d552001-06-28 07:25:16 +00004456 mflag = 0;
4457 optschanged();
Eric Andersenc470f442003-07-28 09:56:35 +00004458 shellexec(argv + 1, pathval(), 0);
Eric Andersencb57d552001-06-28 07:25:16 +00004459 }
4460 return 0;
4461}
4462
Eric Andersenc470f442003-07-28 09:56:35 +00004463
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00004464/* ============ Executing commands */
Eric Andersenc470f442003-07-28 09:56:35 +00004465
4466/*
4467 * When commands are first encountered, they are entered in a hash table.
4468 * This ensures that a full path search will not have to be done for them
4469 * on each invocation.
4470 *
4471 * We should investigate converting to a linear search, even though that
4472 * would make the command name "hash" a misnomer.
4473 */
4474
4475#define CMDTABLESIZE 31 /* should be prime */
4476#define ARB 1 /* actual size determined at run time */
4477
Eric Andersenc470f442003-07-28 09:56:35 +00004478struct tblentry {
4479 struct tblentry *next; /* next entry in hash chain */
4480 union param param; /* definition of builtin function */
4481 short cmdtype; /* index identifying command */
4482 char rehash; /* if set, cd done since entry created */
4483 char cmdname[ARB]; /* name of command */
4484};
4485
Eric Andersenc470f442003-07-28 09:56:35 +00004486static struct tblentry *cmdtable[CMDTABLESIZE];
4487static int builtinloc = -1; /* index in path of %builtin, or -1 */
4488
Eric Andersenc470f442003-07-28 09:56:35 +00004489static void
4490tryexec(char *cmd, char **argv, char **envp)
Eric Andersen62483552001-07-10 06:09:16 +00004491{
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004492 int repeated = 0;
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004493 struct BB_applet *a;
4494 int argc = 0;
4495 char **c;
Denis Vlasenkof7996f32007-01-11 17:20:00 +00004496
Denis Vlasenko8f27c342006-12-26 21:31:11 +00004497 if (strchr(cmd, '/') == NULL
4498 && (a = find_applet_by_name(cmd)) != NULL
4499 && is_safe_applet(cmd)
4500 ) {
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004501 c = argv;
4502 while (*c != NULL) {
4503 c++; argc++;
4504 }
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00004505 applet_name = cmd;
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004506 exit(a->main(argc, argv));
4507 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00004508#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko8f27c342006-12-26 21:31:11 +00004509 if (find_applet_by_name(cmd) != NULL) {
Rob Landley0fcd9432005-05-07 08:27:34 +00004510 /* re-exec ourselves with the new arguments */
Denis Vlasenko131ae172007-02-18 13:00:19 +00004511 execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
Rob Landley0fcd9432005-05-07 08:27:34 +00004512 /* If they called chroot or otherwise made the binary no longer
4513 * executable, fall through */
Glenn L McGrathc00c56e2002-12-23 10:23:10 +00004514 }
Eric Andersen3102ac42001-07-06 04:26:23 +00004515#endif
Eric Andersenc470f442003-07-28 09:56:35 +00004516
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004517 repeat:
Eric Andersenc470f442003-07-28 09:56:35 +00004518#ifdef SYSV
4519 do {
4520 execve(cmd, argv, envp);
4521 } while (errno == EINTR);
4522#else
Eric Andersencb57d552001-06-28 07:25:16 +00004523 execve(cmd, argv, envp);
Eric Andersenc470f442003-07-28 09:56:35 +00004524#endif
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004525 if (repeated++) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00004526 free(argv);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004527 } else if (errno == ENOEXEC) {
4528 char **ap;
4529 char **new;
4530
Eric Andersenc470f442003-07-28 09:56:35 +00004531 for (ap = argv; *ap; ap++)
4532 ;
4533 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
Glenn L McGrath47e5ca12003-09-15 12:00:19 +00004534 ap[1] = cmd;
4535 *ap = cmd = (char *)DEFAULT_SHELL;
4536 ap += 2;
4537 argv++;
Eric Andersenc470f442003-07-28 09:56:35 +00004538 while ((*ap++ = *argv++))
4539 ;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004540 argv = new;
4541 goto repeat;
Eric Andersencb57d552001-06-28 07:25:16 +00004542 }
Eric Andersencb57d552001-06-28 07:25:16 +00004543}
4544
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00004545/*
4546 * Exec a program. Never returns. If you change this routine, you may
4547 * have to change the find_command routine as well.
4548 */
4549#define environment() listvars(VEXPORT, VUNSET, 0)
4550static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
4551static void
4552shellexec(char **argv, const char *path, int idx)
4553{
4554 char *cmdname;
4555 int e;
4556 char **envp;
4557 int exerrno;
Eric Andersenc470f442003-07-28 09:56:35 +00004558
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00004559 clearredir(1);
4560 envp = environment();
4561 if (strchr(argv[0], '/') || is_safe_applet(argv[0])
4562#if ENABLE_FEATURE_SH_STANDALONE_SHELL
4563 || find_applet_by_name(argv[0])
4564#endif
4565 ) {
4566 tryexec(argv[0], argv, envp);
4567 e = errno;
4568 } else {
4569 e = ENOENT;
4570 while ((cmdname = padvance(&path, argv[0])) != NULL) {
4571 if (--idx < 0 && pathopt == NULL) {
4572 tryexec(cmdname, argv, envp);
4573 if (errno != ENOENT && errno != ENOTDIR)
4574 e = errno;
4575 }
4576 stunalloc(cmdname);
4577 }
4578 }
4579
4580 /* Map to POSIX errors */
4581 switch (e) {
4582 case EACCES:
4583 exerrno = 126;
4584 break;
4585 case ENOENT:
4586 exerrno = 127;
4587 break;
4588 default:
4589 exerrno = 2;
4590 break;
4591 }
4592 exitstatus = exerrno;
4593 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
4594 argv[0], e, suppressint ));
4595 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
4596 /* NOTREACHED */
4597}
Eric Andersencb57d552001-06-28 07:25:16 +00004598
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004599static void
4600printentry(struct tblentry *cmdp)
4601{
4602 int idx;
4603 const char *path;
4604 char *name;
4605
4606 idx = cmdp->param.index;
4607 path = pathval();
4608 do {
4609 name = padvance(&path, cmdp->cmdname);
4610 stunalloc(name);
4611 } while (--idx >= 0);
4612 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
4613}
4614
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00004615/*
4616 * Clear out command entries. The argument specifies the first entry in
4617 * PATH which has changed.
4618 */
4619static void
4620clearcmdentry(int firstchange)
4621{
4622 struct tblentry **tblp;
4623 struct tblentry **pp;
4624 struct tblentry *cmdp;
4625
4626 INT_OFF;
4627 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
4628 pp = tblp;
4629 while ((cmdp = *pp) != NULL) {
4630 if ((cmdp->cmdtype == CMDNORMAL &&
4631 cmdp->param.index >= firstchange)
4632 || (cmdp->cmdtype == CMDBUILTIN &&
4633 builtinloc >= firstchange)
4634 ) {
4635 *pp = cmdp->next;
4636 free(cmdp);
4637 } else {
4638 pp = &cmdp->next;
4639 }
4640 }
4641 }
4642 INT_ON;
4643}
4644
4645/*
4646 * Locate a command in the command hash table. If "add" is nonzero,
4647 * add the command to the table if it is not already present. The
4648 * variable "lastcmdentry" is set to point to the address of the link
4649 * pointing to the entry, so that delete_cmd_entry can delete the
4650 * entry.
4651 *
4652 * Interrupts must be off if called with add != 0.
4653 */
4654static struct tblentry **lastcmdentry;
4655
4656static struct tblentry *
4657cmdlookup(const char *name, int add)
4658{
4659 unsigned int hashval;
4660 const char *p;
4661 struct tblentry *cmdp;
4662 struct tblentry **pp;
4663
4664 p = name;
4665 hashval = (unsigned char)*p << 4;
4666 while (*p)
4667 hashval += (unsigned char)*p++;
4668 hashval &= 0x7FFF;
4669 pp = &cmdtable[hashval % CMDTABLESIZE];
4670 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
4671 if (strcmp(cmdp->cmdname, name) == 0)
4672 break;
4673 pp = &cmdp->next;
4674 }
4675 if (add && cmdp == NULL) {
4676 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
4677 + strlen(name) + 1);
4678 cmdp->next = NULL;
4679 cmdp->cmdtype = CMDUNKNOWN;
4680 strcpy(cmdp->cmdname, name);
4681 }
4682 lastcmdentry = pp;
4683 return cmdp;
4684}
4685
4686/*
4687 * Delete the command entry returned on the last lookup.
4688 */
4689static void
4690delete_cmd_entry(void)
4691{
4692 struct tblentry *cmdp;
4693
4694 INT_OFF;
4695 cmdp = *lastcmdentry;
4696 *lastcmdentry = cmdp->next;
4697 if (cmdp->cmdtype == CMDFUNCTION)
4698 freefunc(cmdp->param.func);
4699 free(cmdp);
4700 INT_ON;
4701}
4702
4703/*
4704 * Add a new command entry, replacing any existing command entry for
4705 * the same name - except special builtins.
4706 */
4707static void
4708addcmdentry(char *name, struct cmdentry *entry)
4709{
4710 struct tblentry *cmdp;
4711
4712 cmdp = cmdlookup(name, 1);
4713 if (cmdp->cmdtype == CMDFUNCTION) {
4714 freefunc(cmdp->param.func);
4715 }
4716 cmdp->cmdtype = entry->cmdtype;
4717 cmdp->param = entry->u;
4718 cmdp->rehash = 0;
4719}
Eric Andersenc470f442003-07-28 09:56:35 +00004720
4721static int
4722hashcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004723{
4724 struct tblentry **pp;
4725 struct tblentry *cmdp;
4726 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00004727 struct cmdentry entry;
4728 char *name;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004729
Eric Andersenc470f442003-07-28 09:56:35 +00004730 while ((c = nextopt("r")) != '\0') {
4731 clearcmdentry(0);
4732 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00004733 }
4734 if (*argptr == NULL) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004735 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
4736 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00004737 if (cmdp->cmdtype == CMDNORMAL)
4738 printentry(cmdp);
Eric Andersencb57d552001-06-28 07:25:16 +00004739 }
4740 }
4741 return 0;
4742 }
4743 c = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00004744 while ((name = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004745 cmdp = cmdlookup(name, 0);
4746 if (cmdp != NULL
Eric Andersenc470f442003-07-28 09:56:35 +00004747 && (cmdp->cmdtype == CMDNORMAL
4748 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
Eric Andersencb57d552001-06-28 07:25:16 +00004749 delete_cmd_entry();
4750 find_command(name, &entry, DO_ERR, pathval());
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004751 if (entry.cmdtype == CMDUNKNOWN)
4752 c = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00004753 argptr++;
Eric Andersencb57d552001-06-28 07:25:16 +00004754 }
4755 return c;
4756}
4757
Eric Andersencb57d552001-06-28 07:25:16 +00004758/*
4759 * Resolve a command name. If you change this routine, you may have to
4760 * change the shellexec routine as well.
4761 */
Eric Andersencb57d552001-06-28 07:25:16 +00004762static void
Eric Andersenc470f442003-07-28 09:56:35 +00004763find_command(char *name, struct cmdentry *entry, int act, const char *path)
Eric Andersencb57d552001-06-28 07:25:16 +00004764{
4765 struct tblentry *cmdp;
4766 int idx;
4767 int prev;
4768 char *fullname;
4769 struct stat statb;
4770 int e;
Eric Andersencb57d552001-06-28 07:25:16 +00004771 int updatetbl;
Eric Andersencb57d552001-06-28 07:25:16 +00004772 struct builtincmd *bcmd;
4773
Eric Andersenc470f442003-07-28 09:56:35 +00004774 /* If name contains a slash, don't use PATH or hash table */
Eric Andersencb57d552001-06-28 07:25:16 +00004775 if (strchr(name, '/') != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00004776 entry->u.index = -1;
Eric Andersencb57d552001-06-28 07:25:16 +00004777 if (act & DO_ABS) {
4778 while (stat(name, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00004779#ifdef SYSV
4780 if (errno == EINTR)
4781 continue;
4782#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004783 entry->cmdtype = CMDUNKNOWN;
Eric Andersencb57d552001-06-28 07:25:16 +00004784 return;
4785 }
Eric Andersencb57d552001-06-28 07:25:16 +00004786 }
4787 entry->cmdtype = CMDNORMAL;
Eric Andersencb57d552001-06-28 07:25:16 +00004788 return;
4789 }
4790
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00004791#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Eric Andersenbf8bf102002-09-17 08:41:08 +00004792 if (find_applet_by_name(name)) {
4793 entry->cmdtype = CMDNORMAL;
4794 entry->u.index = -1;
4795 return;
4796 }
4797#endif
4798
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004799 if (is_safe_applet(name)) {
4800 entry->cmdtype = CMDNORMAL;
4801 entry->u.index = -1;
4802 return;
4803 }
4804
Eric Andersenc470f442003-07-28 09:56:35 +00004805 updatetbl = (path == pathval());
4806 if (!updatetbl) {
4807 act |= DO_ALTPATH;
4808 if (strstr(path, "%builtin") != NULL)
4809 act |= DO_ALTBLTIN;
Eric Andersencb57d552001-06-28 07:25:16 +00004810 }
4811
Eric Andersenc470f442003-07-28 09:56:35 +00004812 /* If name is in the table, check answer will be ok */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004813 cmdp = cmdlookup(name, 0);
4814 if (cmdp != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00004815 int bit;
Eric Andersencb57d552001-06-28 07:25:16 +00004816
Eric Andersenc470f442003-07-28 09:56:35 +00004817 switch (cmdp->cmdtype) {
4818 default:
4819#if DEBUG
4820 abort();
4821#endif
4822 case CMDNORMAL:
4823 bit = DO_ALTPATH;
4824 break;
4825 case CMDFUNCTION:
4826 bit = DO_NOFUNC;
4827 break;
4828 case CMDBUILTIN:
4829 bit = DO_ALTBLTIN;
4830 break;
Eric Andersencb57d552001-06-28 07:25:16 +00004831 }
Eric Andersenc470f442003-07-28 09:56:35 +00004832 if (act & bit) {
Eric Andersencb57d552001-06-28 07:25:16 +00004833 updatetbl = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00004834 cmdp = NULL;
4835 } else if (cmdp->rehash == 0)
4836 /* if not invalidated by cd, we're done */
4837 goto success;
Eric Andersencb57d552001-06-28 07:25:16 +00004838 }
4839
4840 /* If %builtin not in path, check for builtin next */
Eric Andersenc470f442003-07-28 09:56:35 +00004841 bcmd = find_builtin(name);
4842 if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
4843 act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
4844 )))
4845 goto builtin_success;
Eric Andersencb57d552001-06-28 07:25:16 +00004846
4847 /* We have to search path. */
Eric Andersenc470f442003-07-28 09:56:35 +00004848 prev = -1; /* where to start */
4849 if (cmdp && cmdp->rehash) { /* doing a rehash */
Eric Andersencb57d552001-06-28 07:25:16 +00004850 if (cmdp->cmdtype == CMDBUILTIN)
4851 prev = builtinloc;
4852 else
4853 prev = cmdp->param.index;
4854 }
4855
4856 e = ENOENT;
4857 idx = -1;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004858 loop:
Eric Andersencb57d552001-06-28 07:25:16 +00004859 while ((fullname = padvance(&path, name)) != NULL) {
4860 stunalloc(fullname);
4861 idx++;
Eric Andersencb57d552001-06-28 07:25:16 +00004862 if (pathopt) {
Eric Andersenc470f442003-07-28 09:56:35 +00004863 if (prefix(pathopt, "builtin")) {
4864 if (bcmd)
4865 goto builtin_success;
Eric Andersencb57d552001-06-28 07:25:16 +00004866 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00004867 } else if (!(act & DO_NOFUNC) &&
4868 prefix(pathopt, "func")) {
Eric Andersencb57d552001-06-28 07:25:16 +00004869 /* handled below */
4870 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00004871 /* ignore unimplemented options */
4872 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00004873 }
4874 }
4875 /* if rehash, don't redo absolute path names */
Eric Andersenc470f442003-07-28 09:56:35 +00004876 if (fullname[0] == '/' && idx <= prev) {
Eric Andersencb57d552001-06-28 07:25:16 +00004877 if (idx < prev)
4878 continue;
4879 TRACE(("searchexec \"%s\": no change\n", name));
4880 goto success;
4881 }
4882 while (stat(fullname, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00004883#ifdef SYSV
4884 if (errno == EINTR)
4885 continue;
4886#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004887 if (errno != ENOENT && errno != ENOTDIR)
4888 e = errno;
4889 goto loop;
4890 }
Eric Andersenc470f442003-07-28 09:56:35 +00004891 e = EACCES; /* if we fail, this will be the error */
Eric Andersencb57d552001-06-28 07:25:16 +00004892 if (!S_ISREG(statb.st_mode))
4893 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00004894 if (pathopt) { /* this is a %func directory */
Eric Andersencb57d552001-06-28 07:25:16 +00004895 stalloc(strlen(fullname) + 1);
4896 readcmdfile(fullname);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004897 cmdp = cmdlookup(name, 0);
4898 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
Denis Vlasenkob012b102007-02-19 22:43:01 +00004899 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
Eric Andersencb57d552001-06-28 07:25:16 +00004900 stunalloc(fullname);
4901 goto success;
4902 }
Eric Andersencb57d552001-06-28 07:25:16 +00004903 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
Eric Andersencb57d552001-06-28 07:25:16 +00004904 if (!updatetbl) {
4905 entry->cmdtype = CMDNORMAL;
4906 entry->u.index = idx;
4907 return;
4908 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004909 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00004910 cmdp = cmdlookup(name, 1);
4911 cmdp->cmdtype = CMDNORMAL;
4912 cmdp->param.index = idx;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004913 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00004914 goto success;
4915 }
4916
4917 /* We failed. If there was an entry for this command, delete it */
4918 if (cmdp && updatetbl)
4919 delete_cmd_entry();
4920 if (act & DO_ERR)
Denis Vlasenkob012b102007-02-19 22:43:01 +00004921 ash_msg("%s: %s", name, errmsg(e, "not found"));
Eric Andersencb57d552001-06-28 07:25:16 +00004922 entry->cmdtype = CMDUNKNOWN;
4923 return;
4924
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004925 builtin_success:
Eric Andersenc470f442003-07-28 09:56:35 +00004926 if (!updatetbl) {
4927 entry->cmdtype = CMDBUILTIN;
4928 entry->u.cmd = bcmd;
4929 return;
4930 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004931 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00004932 cmdp = cmdlookup(name, 1);
4933 cmdp->cmdtype = CMDBUILTIN;
4934 cmdp->param.cmd = bcmd;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004935 INT_ON;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004936 success:
Eric Andersencb57d552001-06-28 07:25:16 +00004937 cmdp->rehash = 0;
4938 entry->cmdtype = cmdp->cmdtype;
4939 entry->u = cmdp->param;
4940}
4941
Eric Andersenc470f442003-07-28 09:56:35 +00004942/*
Eric Andersencb57d552001-06-28 07:25:16 +00004943 * Search the table of builtin commands.
4944 */
Eric Andersenc470f442003-07-28 09:56:35 +00004945static struct builtincmd *
4946find_builtin(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00004947{
4948 struct builtincmd *bp;
4949
Eric Andersenc470f442003-07-28 09:56:35 +00004950 bp = bsearch(
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00004951 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
Eric Andersenc470f442003-07-28 09:56:35 +00004952 pstrcmp
4953 );
Eric Andersencb57d552001-06-28 07:25:16 +00004954 return bp;
4955}
4956
Eric Andersencb57d552001-06-28 07:25:16 +00004957/*
4958 * Called when a cd is done. Marks all commands so the next time they
4959 * are executed they will be rehashed.
4960 */
Eric Andersenc470f442003-07-28 09:56:35 +00004961static void
4962hashcd(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004963{
Eric Andersencb57d552001-06-28 07:25:16 +00004964 struct tblentry **pp;
4965 struct tblentry *cmdp;
4966
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004967 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
4968 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00004969 if (cmdp->cmdtype == CMDNORMAL || (
4970 cmdp->cmdtype == CMDBUILTIN &&
4971 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
4972 builtinloc > 0
4973 ))
Eric Andersencb57d552001-06-28 07:25:16 +00004974 cmdp->rehash = 1;
4975 }
4976 }
4977}
4978
Eric Andersencb57d552001-06-28 07:25:16 +00004979/*
Eric Andersenc470f442003-07-28 09:56:35 +00004980 * Fix command hash table when PATH changed.
Eric Andersencb57d552001-06-28 07:25:16 +00004981 * Called before PATH is changed. The argument is the new value of PATH;
Eric Andersenc470f442003-07-28 09:56:35 +00004982 * pathval() still returns the old value at this point.
4983 * Called with interrupts off.
Eric Andersencb57d552001-06-28 07:25:16 +00004984 */
Eric Andersenc470f442003-07-28 09:56:35 +00004985static void
4986changepath(const char *newval)
Eric Andersencb57d552001-06-28 07:25:16 +00004987{
Eric Andersenc470f442003-07-28 09:56:35 +00004988 const char *old, *new;
4989 int idx;
Eric Andersencb57d552001-06-28 07:25:16 +00004990 int firstchange;
Eric Andersenc470f442003-07-28 09:56:35 +00004991 int idx_bltin;
Eric Andersencb57d552001-06-28 07:25:16 +00004992
Eric Andersenc470f442003-07-28 09:56:35 +00004993 old = pathval();
4994 new = newval;
4995 firstchange = 9999; /* assume no change */
4996 idx = 0;
4997 idx_bltin = -1;
4998 for (;;) {
4999 if (*old != *new) {
5000 firstchange = idx;
5001 if ((*old == '\0' && *new == ':')
5002 || (*old == ':' && *new == '\0'))
5003 firstchange++;
5004 old = new; /* ignore subsequent differences */
5005 }
5006 if (*new == '\0')
5007 break;
5008 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
5009 idx_bltin = idx;
5010 if (*new == ':') {
5011 idx++;
5012 }
5013 new++, old++;
5014 }
5015 if (builtinloc < 0 && idx_bltin >= 0)
5016 builtinloc = idx_bltin; /* zap builtins */
5017 if (builtinloc >= 0 && idx_bltin < 0)
5018 firstchange = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00005019 clearcmdentry(firstchange);
Eric Andersenc470f442003-07-28 09:56:35 +00005020 builtinloc = idx_bltin;
Eric Andersencb57d552001-06-28 07:25:16 +00005021}
5022
5023
5024/*
Eric Andersenc470f442003-07-28 09:56:35 +00005025 * Make a copy of a parse tree.
5026 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00005027static struct funcnode *
5028copyfunc(union node *n)
Eric Andersenc470f442003-07-28 09:56:35 +00005029{
5030 struct funcnode *f;
5031 size_t blocksize;
5032
5033 funcblocksize = offsetof(struct funcnode, n);
5034 funcstringsize = 0;
5035 calcsize(n);
5036 blocksize = funcblocksize;
5037 f = ckmalloc(blocksize + funcstringsize);
5038 funcblock = (char *) f + offsetof(struct funcnode, n);
5039 funcstring = (char *) f + blocksize;
5040 copynode(n);
5041 f->count = 0;
5042 return f;
5043}
5044
5045/*
5046 * Define a shell function.
5047 */
Eric Andersenc470f442003-07-28 09:56:35 +00005048static void
5049defun(char *name, union node *func)
5050{
5051 struct cmdentry entry;
5052
Denis Vlasenkob012b102007-02-19 22:43:01 +00005053 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00005054 entry.cmdtype = CMDFUNCTION;
5055 entry.u.func = copyfunc(func);
5056 addcmdentry(name, &entry);
Denis Vlasenkob012b102007-02-19 22:43:01 +00005057 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00005058}
Eric Andersencb57d552001-06-28 07:25:16 +00005059
Eric Andersencb57d552001-06-28 07:25:16 +00005060/*
5061 * Delete a function if it exists.
5062 */
Eric Andersenc470f442003-07-28 09:56:35 +00005063static void
5064unsetfunc(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00005065{
Eric Andersencb57d552001-06-28 07:25:16 +00005066 struct tblentry *cmdp;
5067
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00005068 cmdp = cmdlookup(name, 0);
5069 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
Eric Andersencb57d552001-06-28 07:25:16 +00005070 delete_cmd_entry();
Eric Andersencb57d552001-06-28 07:25:16 +00005071}
5072
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00005073
Eric Andersen2870d962001-07-02 17:27:21 +00005074/*
Eric Andersencb57d552001-06-28 07:25:16 +00005075 * Locate and print what a word is...
5076 */
Denis Vlasenko131ae172007-02-18 13:00:19 +00005077#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00005078static int
5079describe_command(char *command, int describe_command_verbose)
5080#else
5081#define describe_command_verbose 1
5082static int
5083describe_command(char *command)
5084#endif
5085{
5086 struct cmdentry entry;
5087 struct tblentry *cmdp;
Denis Vlasenko131ae172007-02-18 13:00:19 +00005088#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00005089 const struct alias *ap;
5090#endif
5091 const char *path = pathval();
5092
5093 if (describe_command_verbose) {
5094 out1str(command);
5095 }
5096
5097 /* First look at the keywords */
5098 if (findkwd(command)) {
5099 out1str(describe_command_verbose ? " is a shell keyword" : command);
5100 goto out;
5101 }
5102
Denis Vlasenko131ae172007-02-18 13:00:19 +00005103#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00005104 /* Then look at the aliases */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005105 ap = lookupalias(command, 0);
5106 if (ap != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00005107 if (describe_command_verbose) {
5108 out1fmt(" is an alias for %s", ap->val);
5109 } else {
5110 out1str("alias ");
5111 printalias(ap);
5112 return 0;
5113 }
5114 goto out;
5115 }
5116#endif
5117 /* Then check if it is a tracked alias */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005118 cmdp = cmdlookup(command, 0);
5119 if (cmdp != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00005120 entry.cmdtype = cmdp->cmdtype;
5121 entry.u = cmdp->param;
5122 } else {
5123 /* Finally use brute force */
5124 find_command(command, &entry, DO_ABS, path);
5125 }
5126
5127 switch (entry.cmdtype) {
5128 case CMDNORMAL: {
5129 int j = entry.u.index;
5130 char *p;
5131 if (j == -1) {
5132 p = command;
5133 } else {
5134 do {
5135 p = padvance(&path, command);
5136 stunalloc(p);
5137 } while (--j >= 0);
5138 }
5139 if (describe_command_verbose) {
5140 out1fmt(" is%s %s",
5141 (cmdp ? " a tracked alias for" : nullstr), p
5142 );
5143 } else {
5144 out1str(p);
5145 }
5146 break;
5147 }
5148
5149 case CMDFUNCTION:
5150 if (describe_command_verbose) {
5151 out1str(" is a shell function");
5152 } else {
5153 out1str(command);
5154 }
5155 break;
5156
5157 case CMDBUILTIN:
5158 if (describe_command_verbose) {
5159 out1fmt(" is a %sshell builtin",
5160 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
5161 "special " : nullstr
5162 );
5163 } else {
5164 out1str(command);
5165 }
5166 break;
5167
5168 default:
5169 if (describe_command_verbose) {
5170 out1str(": not found\n");
5171 }
5172 return 127;
5173 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005174 out:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00005175 outstr("\n", stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00005176 return 0;
5177}
5178
5179static int
5180typecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00005181{
5182 int i;
5183 int err = 0;
5184
5185 for (i = 1; i < argc; i++) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00005186#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00005187 err |= describe_command(argv[i], 1);
5188#else
5189 err |= describe_command(argv[i]);
5190#endif
Eric Andersencb57d552001-06-28 07:25:16 +00005191 }
5192 return err;
5193}
5194
Denis Vlasenko131ae172007-02-18 13:00:19 +00005195#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00005196static int
5197commandcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00005198{
5199 int c;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005200 enum {
5201 VERIFY_BRIEF = 1,
5202 VERIFY_VERBOSE = 2,
5203 } verify = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00005204
5205 while ((c = nextopt("pvV")) != '\0')
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005206 if (c == 'V')
5207 verify |= VERIFY_VERBOSE;
5208 else if (c == 'v')
5209 verify |= VERIFY_BRIEF;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00005210#if DEBUG
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005211 else if (c != 'p')
5212 abort();
Eric Andersenc470f442003-07-28 09:56:35 +00005213#endif
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005214 if (verify)
5215 return describe_command(*argptr, verify - VERIFY_BRIEF);
Eric Andersencb57d552001-06-28 07:25:16 +00005216
5217 return 0;
5218}
Eric Andersen2870d962001-07-02 17:27:21 +00005219#endif
Eric Andersencb57d552001-06-28 07:25:16 +00005220
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005221/* expand.c */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005222
Eric Andersencb57d552001-06-28 07:25:16 +00005223/*
5224 * Routines to expand arguments to commands. We have to deal with
5225 * backquotes, shell variables, and file metacharacters.
5226 */
Eric Andersenc470f442003-07-28 09:56:35 +00005227
Eric Andersencb57d552001-06-28 07:25:16 +00005228/*
5229 * _rmescape() flags
5230 */
Eric Andersenc470f442003-07-28 09:56:35 +00005231#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5232#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5233#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5234#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5235#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
Eric Andersencb57d552001-06-28 07:25:16 +00005236
5237/*
5238 * Structure specifying which parts of the string should be searched
5239 * for IFS characters.
5240 */
5241
5242struct ifsregion {
Eric Andersenc470f442003-07-28 09:56:35 +00005243 struct ifsregion *next; /* next region in list */
5244 int begoff; /* offset of start of region */
5245 int endoff; /* offset of end of region */
5246 int nulonly; /* search for nul bytes only */
Eric Andersencb57d552001-06-28 07:25:16 +00005247};
5248
Eric Andersenc470f442003-07-28 09:56:35 +00005249/* output of current string */
5250static char *expdest;
5251/* list of back quote expressions */
5252static struct nodelist *argbackq;
5253/* first struct in list of ifs regions */
5254static struct ifsregion ifsfirst;
5255/* last struct in list */
5256static struct ifsregion *ifslastp;
5257/* holds expanded arg list */
5258static struct arglist exparg;
Eric Andersencb57d552001-06-28 07:25:16 +00005259
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005260static void argstr(char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005261static char *exptilde(char *, char *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005262static void expbackq(union node *, int, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005263static const char *subevalvar(char *, char *, int, int, int, int, int);
5264static char *evalvar(char *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005265static void strtodest(const char *, int, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005266static void memtodest(const char *p, size_t len, int syntax, int quotes);
Glenn L McGrath76620622004-01-13 10:19:37 +00005267static ssize_t varvalue(char *, int, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005268static void recordregion(int, int, int);
5269static void removerecordregions(int);
5270static void ifsbreakup(char *, struct arglist *);
5271static void ifsfree(void);
5272static void expandmeta(struct strlist *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005273static int patmatch(char *, const char *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005274
Eric Andersened9ecf72004-06-22 08:29:45 +00005275static int cvtnum(arith_t);
Eric Andersenc470f442003-07-28 09:56:35 +00005276static size_t esclen(const char *, const char *);
5277static char *scanleft(char *, char *, char *, char *, int, int);
5278static char *scanright(char *, char *, char *, char *, int, int);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00005279static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005280
Eric Andersenc470f442003-07-28 09:56:35 +00005281
5282#define pmatch(a, b) !fnmatch((a), (b), 0)
5283/*
Eric Andersen90898442003-08-06 11:20:52 +00005284 * Prepare a pattern for a expmeta (internal glob(3)) call.
Eric Andersenc470f442003-07-28 09:56:35 +00005285 *
5286 * Returns an stalloced string.
5287 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00005288static char *
5289preglob(const char *pattern, int quoted, int flag)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005290{
Eric Andersenc470f442003-07-28 09:56:35 +00005291 flag |= RMESCAPE_GLOB;
5292 if (quoted) {
5293 flag |= RMESCAPE_QUOTED;
5294 }
5295 return _rmescapes((char *)pattern, flag);
5296}
5297
5298
5299static size_t
Denis Vlasenkoe5570da2007-02-19 22:41:55 +00005300esclen(const char *start, const char *p)
5301{
Eric Andersenc470f442003-07-28 09:56:35 +00005302 size_t esc = 0;
5303
5304 while (p > start && *--p == CTLESC) {
5305 esc++;
5306 }
5307 return esc;
5308}
5309
Eric Andersencb57d552001-06-28 07:25:16 +00005310
5311/*
5312 * Expand shell variables and backquotes inside a here document.
5313 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00005314static void
5315expandhere(union node *arg, int fd)
Eric Andersen2870d962001-07-02 17:27:21 +00005316{
Eric Andersencb57d552001-06-28 07:25:16 +00005317 herefd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +00005318 expandarg(arg, (struct arglist *)NULL, 0);
Rob Landley53437472006-07-16 08:14:35 +00005319 full_write(fd, stackblock(), expdest - (char *)stackblock());
Eric Andersencb57d552001-06-28 07:25:16 +00005320}
5321
5322
5323/*
5324 * Perform variable substitution and command substitution on an argument,
5325 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
5326 * perform splitting and file name expansion. When arglist is NULL, perform
5327 * here document expansion.
5328 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005329static void
Eric Andersenc470f442003-07-28 09:56:35 +00005330expandarg(union node *arg, struct arglist *arglist, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005331{
5332 struct strlist *sp;
5333 char *p;
5334
5335 argbackq = arg->narg.backquote;
5336 STARTSTACKSTR(expdest);
5337 ifsfirst.next = NULL;
5338 ifslastp = NULL;
5339 argstr(arg->narg.text, flag);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00005340 p = _STPUTC('\0', expdest);
5341 expdest = p - 1;
Eric Andersencb57d552001-06-28 07:25:16 +00005342 if (arglist == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00005343 return; /* here document expanded */
Eric Andersencb57d552001-06-28 07:25:16 +00005344 }
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00005345 p = grabstackstr(p);
Eric Andersencb57d552001-06-28 07:25:16 +00005346 exparg.lastp = &exparg.list;
5347 /*
5348 * TODO - EXP_REDIR
5349 */
5350 if (flag & EXP_FULL) {
5351 ifsbreakup(p, &exparg);
5352 *exparg.lastp = NULL;
5353 exparg.lastp = &exparg.list;
5354 expandmeta(exparg.list, flag);
5355 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00005356 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Eric Andersencb57d552001-06-28 07:25:16 +00005357 rmescapes(p);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00005358 sp = stalloc(sizeof(*sp));
Eric Andersencb57d552001-06-28 07:25:16 +00005359 sp->text = p;
5360 *exparg.lastp = sp;
5361 exparg.lastp = &sp->next;
5362 }
Eric Andersenc470f442003-07-28 09:56:35 +00005363 if (ifsfirst.next)
5364 ifsfree();
Eric Andersencb57d552001-06-28 07:25:16 +00005365 *exparg.lastp = NULL;
5366 if (exparg.list) {
5367 *arglist->lastp = exparg.list;
5368 arglist->lastp = exparg.lastp;
5369 }
5370}
5371
5372
Eric Andersenc470f442003-07-28 09:56:35 +00005373/*
5374 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5375 * characters to allow for further processing. Otherwise treat
5376 * $@ like $* since no splitting will be performed.
5377 */
Eric Andersenc470f442003-07-28 09:56:35 +00005378static void
5379argstr(char *p, int flag)
5380{
5381 static const char spclchars[] = {
5382 '=',
5383 ':',
5384 CTLQUOTEMARK,
5385 CTLENDVAR,
5386 CTLESC,
5387 CTLVAR,
5388 CTLBACKQ,
5389 CTLBACKQ | CTLQUOTE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00005390#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005391 CTLENDARI,
5392#endif
5393 0
5394 };
5395 const char *reject = spclchars;
5396 int c;
5397 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5398 int breakall = flag & EXP_WORD;
5399 int inquotes;
5400 size_t length;
5401 int startloc;
5402
5403 if (!(flag & EXP_VARTILDE)) {
5404 reject += 2;
5405 } else if (flag & EXP_VARTILDE2) {
5406 reject++;
5407 }
5408 inquotes = 0;
5409 length = 0;
5410 if (flag & EXP_TILDE) {
5411 char *q;
5412
5413 flag &= ~EXP_TILDE;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005414 tilde:
Eric Andersenc470f442003-07-28 09:56:35 +00005415 q = p;
5416 if (*q == CTLESC && (flag & EXP_QWORD))
5417 q++;
5418 if (*q == '~')
5419 p = exptilde(p, q, flag);
5420 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005421 start:
Eric Andersenc470f442003-07-28 09:56:35 +00005422 startloc = expdest - (char *)stackblock();
5423 for (;;) {
5424 length += strcspn(p + length, reject);
5425 c = p[length];
5426 if (c && (!(c & 0x80)
Denis Vlasenko131ae172007-02-18 13:00:19 +00005427#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005428 || c == CTLENDARI
5429#endif
5430 )) {
5431 /* c == '=' || c == ':' || c == CTLENDARI */
5432 length++;
5433 }
5434 if (length > 0) {
5435 int newloc;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00005436 expdest = stack_nputstr(p, length, expdest);
Eric Andersenc470f442003-07-28 09:56:35 +00005437 newloc = expdest - (char *)stackblock();
5438 if (breakall && !inquotes && newloc > startloc) {
5439 recordregion(startloc, newloc, 0);
5440 }
5441 startloc = newloc;
5442 }
5443 p += length + 1;
5444 length = 0;
5445
5446 switch (c) {
5447 case '\0':
5448 goto breakloop;
5449 case '=':
5450 if (flag & EXP_VARTILDE2) {
5451 p--;
5452 continue;
5453 }
5454 flag |= EXP_VARTILDE2;
5455 reject++;
5456 /* fall through */
5457 case ':':
5458 /*
5459 * sort of a hack - expand tildes in variable
5460 * assignments (after the first '=' and after ':'s).
5461 */
5462 if (*--p == '~') {
5463 goto tilde;
5464 }
5465 continue;
5466 }
5467
5468 switch (c) {
5469 case CTLENDVAR: /* ??? */
5470 goto breakloop;
5471 case CTLQUOTEMARK:
5472 /* "$@" syntax adherence hack */
5473 if (
5474 !inquotes &&
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00005475 !memcmp(p, dolatstr, 4) &&
Eric Andersenc470f442003-07-28 09:56:35 +00005476 (p[4] == CTLQUOTEMARK || (
5477 p[4] == CTLENDVAR &&
5478 p[5] == CTLQUOTEMARK
5479 ))
5480 ) {
5481 p = evalvar(p + 1, flag) + 1;
5482 goto start;
5483 }
5484 inquotes = !inquotes;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005485 addquote:
Eric Andersenc470f442003-07-28 09:56:35 +00005486 if (quotes) {
5487 p--;
5488 length++;
5489 startloc++;
5490 }
5491 break;
5492 case CTLESC:
5493 startloc++;
5494 length++;
5495 goto addquote;
5496 case CTLVAR:
5497 p = evalvar(p, flag);
5498 goto start;
5499 case CTLBACKQ:
5500 c = 0;
5501 case CTLBACKQ|CTLQUOTE:
5502 expbackq(argbackq->n, c, quotes);
5503 argbackq = argbackq->next;
5504 goto start;
Denis Vlasenko131ae172007-02-18 13:00:19 +00005505#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005506 case CTLENDARI:
5507 p--;
5508 expari(quotes);
5509 goto start;
5510#endif
5511 }
5512 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005513 breakloop:
Eric Andersenc470f442003-07-28 09:56:35 +00005514 ;
5515}
5516
5517static char *
5518exptilde(char *startp, char *p, int flag)
5519{
5520 char c;
5521 char *name;
5522 struct passwd *pw;
5523 const char *home;
5524 int quotes = flag & (EXP_FULL | EXP_CASE);
5525 int startloc;
5526
5527 name = p + 1;
5528
5529 while ((c = *++p) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00005530 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +00005531 case CTLESC:
Denis Vlasenko097c3242006-11-27 16:59:15 +00005532 return startp;
Eric Andersenc470f442003-07-28 09:56:35 +00005533 case CTLQUOTEMARK:
Denis Vlasenko097c3242006-11-27 16:59:15 +00005534 return startp;
Eric Andersenc470f442003-07-28 09:56:35 +00005535 case ':':
5536 if (flag & EXP_VARTILDE)
5537 goto done;
5538 break;
5539 case '/':
5540 case CTLENDVAR:
5541 goto done;
5542 }
5543 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005544 done:
Eric Andersenc470f442003-07-28 09:56:35 +00005545 *p = '\0';
5546 if (*name == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005547 home = lookupvar(homestr);
Eric Andersenc470f442003-07-28 09:56:35 +00005548 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005549 pw = getpwnam(name);
5550 if (pw == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00005551 goto lose;
5552 home = pw->pw_dir;
5553 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005554 if (!home || !*home)
Eric Andersenc470f442003-07-28 09:56:35 +00005555 goto lose;
5556 *p = c;
5557 startloc = expdest - (char *)stackblock();
5558 strtodest(home, SQSYNTAX, quotes);
5559 recordregion(startloc, expdest - (char *)stackblock(), 0);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00005560 return p;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005561 lose:
Eric Andersenc470f442003-07-28 09:56:35 +00005562 *p = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00005563 return startp;
Eric Andersenc470f442003-07-28 09:56:35 +00005564}
5565
5566
5567static void
5568removerecordregions(int endoff)
5569{
5570 if (ifslastp == NULL)
5571 return;
5572
5573 if (ifsfirst.endoff > endoff) {
5574 while (ifsfirst.next != NULL) {
5575 struct ifsregion *ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005576 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00005577 ifsp = ifsfirst.next->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005578 free(ifsfirst.next);
Eric Andersenc470f442003-07-28 09:56:35 +00005579 ifsfirst.next = ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005580 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00005581 }
5582 if (ifsfirst.begoff > endoff)
5583 ifslastp = NULL;
5584 else {
5585 ifslastp = &ifsfirst;
5586 ifsfirst.endoff = endoff;
5587 }
5588 return;
5589 }
5590
5591 ifslastp = &ifsfirst;
5592 while (ifslastp->next && ifslastp->next->begoff < endoff)
5593 ifslastp=ifslastp->next;
5594 while (ifslastp->next != NULL) {
5595 struct ifsregion *ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005596 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00005597 ifsp = ifslastp->next->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005598 free(ifslastp->next);
Eric Andersenc470f442003-07-28 09:56:35 +00005599 ifslastp->next = ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005600 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00005601 }
5602 if (ifslastp->endoff > endoff)
5603 ifslastp->endoff = endoff;
5604}
5605
5606
Denis Vlasenko131ae172007-02-18 13:00:19 +00005607#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005608/*
5609 * Expand arithmetic expression. Backup to start of expression,
5610 * evaluate, place result in (backed up) result, adjust string position.
5611 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00005612static void
Eric Andersenc470f442003-07-28 09:56:35 +00005613expari(int quotes)
5614{
5615 char *p, *start;
5616 int begoff;
5617 int flag;
5618 int len;
5619
5620 /* ifsfree(); */
5621
5622 /*
5623 * This routine is slightly over-complicated for
5624 * efficiency. Next we scan backwards looking for the
5625 * start of arithmetic.
5626 */
5627 start = stackblock();
5628 p = expdest - 1;
5629 *p = '\0';
5630 p--;
5631 do {
5632 int esc;
5633
5634 while (*p != CTLARI) {
5635 p--;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00005636#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00005637 if (p < start) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00005638 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
Eric Andersenc470f442003-07-28 09:56:35 +00005639 }
5640#endif
5641 }
5642
5643 esc = esclen(start, p);
5644 if (!(esc % 2)) {
5645 break;
5646 }
5647
5648 p -= esc + 1;
5649 } while (1);
5650
5651 begoff = p - start;
5652
5653 removerecordregions(begoff);
5654
5655 flag = p[1];
5656
5657 expdest = p;
5658
5659 if (quotes)
5660 rmescapes(p + 2);
5661
5662 len = cvtnum(dash_arith(p + 2));
5663
5664 if (flag != '"')
5665 recordregion(begoff, begoff + len, 0);
5666}
5667#endif
5668
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005669
Eric Andersenc470f442003-07-28 09:56:35 +00005670/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00005671 * Execute a command inside back quotes. If it's a builtin command, we
5672 * want to save its output in a block obtained from malloc. Otherwise
5673 * we fork off a subprocess and get the output of the command via a pipe.
5674 * Should be called with interrupts off.
5675 */
5676struct backcmd { /* result of evalbackcmd */
5677 int fd; /* file descriptor to read from */
5678 char *buf; /* buffer */
5679 int nleft; /* number of chars in buffer */
5680 struct job *jp; /* job structure for command */
5681};
5682
5683static void
5684evalbackcmd(union node *n, struct backcmd *result)
5685{
5686 int saveherefd;
5687
5688 result->fd = -1;
5689 result->buf = NULL;
5690 result->nleft = 0;
5691 result->jp = NULL;
5692 if (n == NULL) {
5693 goto out;
5694 }
5695
5696 saveherefd = herefd;
5697 herefd = -1;
5698
5699 {
5700 int pip[2];
5701 struct job *jp;
5702
5703 if (pipe(pip) < 0)
5704 ash_msg_and_raise_error("Pipe call failed");
5705 jp = makejob(n, 1);
5706 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5707 FORCE_INT_ON;
5708 close(pip[0]);
5709 if (pip[1] != 1) {
5710 close(1);
5711 copyfd(pip[1], 1);
5712 close(pip[1]);
5713 }
5714 eflag = 0;
5715 evaltreenr(n, EV_EXIT);
5716 /* NOTREACHED */
5717 }
5718 close(pip[1]);
5719 result->fd = pip[0];
5720 result->jp = jp;
5721 }
5722 herefd = saveherefd;
5723 out:
5724 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5725 result->fd, result->buf, result->nleft, result->jp));
5726}
5727
5728/*
Eric Andersenc470f442003-07-28 09:56:35 +00005729 * Expand stuff in backwards quotes.
5730 */
Eric Andersenc470f442003-07-28 09:56:35 +00005731static void
5732expbackq(union node *cmd, int quoted, int quotes)
5733{
5734 struct backcmd in;
5735 int i;
5736 char buf[128];
5737 char *p;
5738 char *dest;
5739 int startloc;
5740 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5741 struct stackmark smark;
5742
Denis Vlasenkob012b102007-02-19 22:43:01 +00005743 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00005744 setstackmark(&smark);
5745 dest = expdest;
5746 startloc = dest - (char *)stackblock();
5747 grabstackstr(dest);
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00005748 evalbackcmd(cmd, &in);
Eric Andersenc470f442003-07-28 09:56:35 +00005749 popstackmark(&smark);
5750
5751 p = in.buf;
5752 i = in.nleft;
5753 if (i == 0)
5754 goto read;
5755 for (;;) {
5756 memtodest(p, i, syntax, quotes);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005757 read:
Eric Andersenc470f442003-07-28 09:56:35 +00005758 if (in.fd < 0)
5759 break;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00005760 i = safe_read(in.fd, buf, sizeof(buf));
Eric Andersenc470f442003-07-28 09:56:35 +00005761 TRACE(("expbackq: read returns %d\n", i));
5762 if (i <= 0)
5763 break;
5764 p = buf;
5765 }
5766
5767 if (in.buf)
Denis Vlasenkob012b102007-02-19 22:43:01 +00005768 free(in.buf);
Eric Andersenc470f442003-07-28 09:56:35 +00005769 if (in.fd >= 0) {
5770 close(in.fd);
5771 back_exitstatus = waitforjob(in.jp);
5772 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00005773 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00005774
5775 /* Eat all trailing newlines */
5776 dest = expdest;
5777 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5778 STUNPUTC(dest);
5779 expdest = dest;
5780
5781 if (quoted == 0)
5782 recordregion(startloc, dest - (char *)stackblock(), 0);
5783 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5784 (dest - (char *)stackblock()) - startloc,
5785 (dest - (char *)stackblock()) - startloc,
5786 stackblock() + startloc));
5787}
5788
5789
5790static char *
Eric Andersen90898442003-08-06 11:20:52 +00005791scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5792 int zero)
5793{
Eric Andersenc470f442003-07-28 09:56:35 +00005794 char *loc;
5795 char *loc2;
5796 char c;
5797
5798 loc = startp;
5799 loc2 = rmesc;
5800 do {
5801 int match;
5802 const char *s = loc2;
5803 c = *loc2;
5804 if (zero) {
5805 *loc2 = '\0';
5806 s = rmesc;
5807 }
5808 match = pmatch(str, s);
5809 *loc2 = c;
5810 if (match)
5811 return loc;
5812 if (quotes && *loc == CTLESC)
5813 loc++;
5814 loc++;
5815 loc2++;
5816 } while (c);
5817 return 0;
5818}
5819
5820
5821static char *
Eric Andersen90898442003-08-06 11:20:52 +00005822scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5823 int zero)
5824{
Eric Andersenc470f442003-07-28 09:56:35 +00005825 int esc = 0;
5826 char *loc;
5827 char *loc2;
5828
5829 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5830 int match;
5831 char c = *loc2;
5832 const char *s = loc2;
5833 if (zero) {
5834 *loc2 = '\0';
5835 s = rmesc;
5836 }
5837 match = pmatch(str, s);
5838 *loc2 = c;
5839 if (match)
5840 return loc;
5841 loc--;
5842 if (quotes) {
5843 if (--esc < 0) {
5844 esc = esclen(startp, loc);
5845 }
5846 if (esc % 2) {
5847 esc--;
5848 loc--;
5849 }
5850 }
5851 }
5852 return 0;
5853}
5854
5855static const char *
5856subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5857{
5858 char *startp;
5859 char *loc;
5860 int saveherefd = herefd;
5861 struct nodelist *saveargbackq = argbackq;
5862 int amount;
5863 char *rmesc, *rmescend;
5864 int zero;
5865 char *(*scan)(char *, char *, char *, char *, int , int);
5866
5867 herefd = -1;
5868 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5869 STPUTC('\0', expdest);
5870 herefd = saveherefd;
5871 argbackq = saveargbackq;
5872 startp = stackblock() + startloc;
5873
5874 switch (subtype) {
5875 case VSASSIGN:
5876 setvar(str, startp, 0);
5877 amount = startp - expdest;
5878 STADJUST(amount, expdest);
5879 return startp;
5880
5881 case VSQUESTION:
5882 varunset(p, str, startp, varflags);
5883 /* NOTREACHED */
5884 }
5885
5886 subtype -= VSTRIMRIGHT;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00005887#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00005888 if (subtype < 0 || subtype > 3)
5889 abort();
5890#endif
5891
5892 rmesc = startp;
5893 rmescend = stackblock() + strloc;
5894 if (quotes) {
5895 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5896 if (rmesc != startp) {
5897 rmescend = expdest;
5898 startp = stackblock() + startloc;
5899 }
5900 }
5901 rmescend--;
5902 str = stackblock() + strloc;
5903 preglob(str, varflags & VSQUOTE, 0);
5904
5905 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5906 zero = subtype >> 1;
5907 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5908 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5909
5910 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5911 if (loc) {
5912 if (zero) {
5913 memmove(startp, loc, str - loc);
5914 loc = startp + (str - loc) - 1;
5915 }
5916 *loc = '\0';
5917 amount = loc - expdest;
5918 STADJUST(amount, expdest);
5919 }
5920 return loc;
5921}
5922
5923
Eric Andersen62483552001-07-10 06:09:16 +00005924/*
5925 * Expand a variable, and return a pointer to the next character in the
5926 * input string.
5927 */
Eric Andersenc470f442003-07-28 09:56:35 +00005928static char *
5929evalvar(char *p, int flag)
Eric Andersen62483552001-07-10 06:09:16 +00005930{
5931 int subtype;
5932 int varflags;
5933 char *var;
Eric Andersen62483552001-07-10 06:09:16 +00005934 int patloc;
5935 int c;
Eric Andersen62483552001-07-10 06:09:16 +00005936 int startloc;
Glenn L McGrath76620622004-01-13 10:19:37 +00005937 ssize_t varlen;
Eric Andersen62483552001-07-10 06:09:16 +00005938 int easy;
Eric Andersenc470f442003-07-28 09:56:35 +00005939 int quotes;
5940 int quoted;
Eric Andersen62483552001-07-10 06:09:16 +00005941
Eric Andersenc470f442003-07-28 09:56:35 +00005942 quotes = flag & (EXP_FULL | EXP_CASE);
Eric Andersen62483552001-07-10 06:09:16 +00005943 varflags = *p++;
5944 subtype = varflags & VSTYPE;
Eric Andersenc470f442003-07-28 09:56:35 +00005945 quoted = varflags & VSQUOTE;
Eric Andersen62483552001-07-10 06:09:16 +00005946 var = p;
Eric Andersenc470f442003-07-28 09:56:35 +00005947 easy = (!quoted || (*var == '@' && shellparam.nparam));
Eric Andersenc470f442003-07-28 09:56:35 +00005948 startloc = expdest - (char *)stackblock();
5949 p = strchr(p, '=') + 1;
5950
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005951 again:
Glenn L McGrath76620622004-01-13 10:19:37 +00005952 varlen = varvalue(var, varflags, flag);
5953 if (varflags & VSNUL)
5954 varlen--;
Eric Andersen62483552001-07-10 06:09:16 +00005955
Glenn L McGrath76620622004-01-13 10:19:37 +00005956 if (subtype == VSPLUS) {
5957 varlen = -1 - varlen;
5958 goto vsplus;
5959 }
Eric Andersen62483552001-07-10 06:09:16 +00005960
Eric Andersenc470f442003-07-28 09:56:35 +00005961 if (subtype == VSMINUS) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005962 vsplus:
Glenn L McGrath76620622004-01-13 10:19:37 +00005963 if (varlen < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00005964 argstr(
5965 p, flag | EXP_TILDE |
5966 (quoted ? EXP_QWORD : EXP_WORD)
5967 );
5968 goto end;
Eric Andersen62483552001-07-10 06:09:16 +00005969 }
5970 if (easy)
5971 goto record;
Eric Andersenc470f442003-07-28 09:56:35 +00005972 goto end;
5973 }
Eric Andersen62483552001-07-10 06:09:16 +00005974
Eric Andersenc470f442003-07-28 09:56:35 +00005975 if (subtype == VSASSIGN || subtype == VSQUESTION) {
Glenn L McGrath76620622004-01-13 10:19:37 +00005976 if (varlen < 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00005977 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
Eric Andersen62483552001-07-10 06:09:16 +00005978 varflags &= ~VSNUL;
5979 /*
5980 * Remove any recorded regions beyond
5981 * start of variable
5982 */
5983 removerecordregions(startloc);
5984 goto again;
5985 }
Eric Andersenc470f442003-07-28 09:56:35 +00005986 goto end;
Eric Andersen62483552001-07-10 06:09:16 +00005987 }
5988 if (easy)
5989 goto record;
Eric Andersenc470f442003-07-28 09:56:35 +00005990 goto end;
Eric Andersen62483552001-07-10 06:09:16 +00005991 }
5992
Glenn L McGrath76620622004-01-13 10:19:37 +00005993 if (varlen < 0 && uflag)
Eric Andersenc470f442003-07-28 09:56:35 +00005994 varunset(p, var, 0, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005995
Eric Andersenc470f442003-07-28 09:56:35 +00005996 if (subtype == VSLENGTH) {
Glenn L McGrath76620622004-01-13 10:19:37 +00005997 cvtnum(varlen > 0 ? varlen : 0);
Eric Andersenc470f442003-07-28 09:56:35 +00005998 goto record;
5999 }
6000
6001 if (subtype == VSNORMAL) {
6002 if (!easy)
6003 goto end;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006004 record:
Eric Andersenc470f442003-07-28 09:56:35 +00006005 recordregion(startloc, expdest - (char *)stackblock(), quoted);
6006 goto end;
6007 }
6008
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00006009#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00006010 switch (subtype) {
6011 case VSTRIMLEFT:
6012 case VSTRIMLEFTMAX:
6013 case VSTRIMRIGHT:
6014 case VSTRIMRIGHTMAX:
6015 break;
6016 default:
6017 abort();
6018 }
6019#endif
6020
Glenn L McGrath76620622004-01-13 10:19:37 +00006021 if (varlen >= 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00006022 /*
6023 * Terminate the string and start recording the pattern
6024 * right after it
6025 */
6026 STPUTC('\0', expdest);
6027 patloc = expdest - (char *)stackblock();
6028 if (subevalvar(p, NULL, patloc, subtype,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00006029 startloc, varflags, quotes) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00006030 int amount = expdest - (
6031 (char *)stackblock() + patloc - 1
6032 );
6033 STADJUST(-amount, expdest);
6034 }
6035 /* Remove any recorded regions beyond start of variable */
6036 removerecordregions(startloc);
6037 goto record;
6038 }
6039
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006040 end:
Eric Andersenc470f442003-07-28 09:56:35 +00006041 if (subtype != VSNORMAL) { /* skip to end of alternative */
6042 int nesting = 1;
Eric Andersen62483552001-07-10 06:09:16 +00006043 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006044 c = *p++;
6045 if (c == CTLESC)
Eric Andersen62483552001-07-10 06:09:16 +00006046 p++;
Eric Andersenc470f442003-07-28 09:56:35 +00006047 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
Glenn L McGrath76620622004-01-13 10:19:37 +00006048 if (varlen >= 0)
Eric Andersen62483552001-07-10 06:09:16 +00006049 argbackq = argbackq->next;
6050 } else if (c == CTLVAR) {
6051 if ((*p++ & VSTYPE) != VSNORMAL)
6052 nesting++;
6053 } else if (c == CTLENDVAR) {
6054 if (--nesting == 0)
6055 break;
6056 }
6057 }
6058 }
6059 return p;
6060}
6061
Eric Andersencb57d552001-06-28 07:25:16 +00006062
Eric Andersencb57d552001-06-28 07:25:16 +00006063/*
6064 * Put a string on the stack.
6065 */
Eric Andersenc470f442003-07-28 09:56:35 +00006066static void
Denis Vlasenkoe5570da2007-02-19 22:41:55 +00006067memtodest(const char *p, size_t len, int syntax, int quotes)
6068{
Eric Andersenc470f442003-07-28 09:56:35 +00006069 char *q = expdest;
6070
6071 q = makestrspace(len * 2, q);
6072
6073 while (len--) {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00006074 int c = SC2INT(*p++);
Eric Andersenc470f442003-07-28 09:56:35 +00006075 if (!c)
6076 continue;
6077 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
6078 USTPUTC(CTLESC, q);
6079 USTPUTC(c, q);
Eric Andersencb57d552001-06-28 07:25:16 +00006080 }
Eric Andersenc470f442003-07-28 09:56:35 +00006081
6082 expdest = q;
Eric Andersencb57d552001-06-28 07:25:16 +00006083}
6084
Eric Andersenc470f442003-07-28 09:56:35 +00006085
6086static void
6087strtodest(const char *p, int syntax, int quotes)
6088{
6089 memtodest(p, strlen(p), syntax, quotes);
6090}
6091
6092
Eric Andersencb57d552001-06-28 07:25:16 +00006093/*
6094 * Add the value of a specialized variable to the stack string.
6095 */
Glenn L McGrath76620622004-01-13 10:19:37 +00006096static ssize_t
6097varvalue(char *name, int varflags, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00006098{
6099 int num;
6100 char *p;
6101 int i;
Glenn L McGrath76620622004-01-13 10:19:37 +00006102 int sep = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00006103 int sepq = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +00006104 ssize_t len = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00006105 char **ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006106 int syntax;
Glenn L McGrath76620622004-01-13 10:19:37 +00006107 int quoted = varflags & VSQUOTE;
6108 int subtype = varflags & VSTYPE;
Eric Andersencb57d552001-06-28 07:25:16 +00006109 int quotes = flags & (EXP_FULL | EXP_CASE);
6110
Glenn L McGrath76620622004-01-13 10:19:37 +00006111 if (quoted && (flags & EXP_FULL))
6112 sep = 1 << CHAR_BIT;
6113
Eric Andersencb57d552001-06-28 07:25:16 +00006114 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6115 switch (*name) {
6116 case '$':
6117 num = rootpid;
6118 goto numvar;
6119 case '?':
Eric Andersenc470f442003-07-28 09:56:35 +00006120 num = exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00006121 goto numvar;
6122 case '#':
6123 num = shellparam.nparam;
6124 goto numvar;
6125 case '!':
6126 num = backgndpid;
Glenn L McGrath76620622004-01-13 10:19:37 +00006127 if (num == 0)
6128 return -1;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006129 numvar:
Glenn L McGrath76620622004-01-13 10:19:37 +00006130 len = cvtnum(num);
Eric Andersencb57d552001-06-28 07:25:16 +00006131 break;
6132 case '-':
Glenn L McGrath76620622004-01-13 10:19:37 +00006133 p = makestrspace(NOPTS, expdest);
6134 for (i = NOPTS - 1; i >= 0; i--) {
6135 if (optlist[i]) {
6136 USTPUTC(optletters(i), p);
6137 len++;
6138 }
Eric Andersencb57d552001-06-28 07:25:16 +00006139 }
Glenn L McGrath76620622004-01-13 10:19:37 +00006140 expdest = p;
Eric Andersencb57d552001-06-28 07:25:16 +00006141 break;
6142 case '@':
Glenn L McGrath76620622004-01-13 10:19:37 +00006143 if (sep)
Eric Andersencb57d552001-06-28 07:25:16 +00006144 goto param;
Eric Andersencb57d552001-06-28 07:25:16 +00006145 /* fall through */
6146 case '*':
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00006147 sep = ifsset() ? SC2INT(ifsval()[0]) : ' ';
Glenn L McGrath76620622004-01-13 10:19:37 +00006148 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6149 sepq = 1;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006150 param:
6151 ap = shellparam.p;
6152 if (!ap)
Glenn L McGrath76620622004-01-13 10:19:37 +00006153 return -1;
6154 while ((p = *ap++)) {
6155 size_t partlen;
6156
6157 partlen = strlen(p);
Glenn L McGrath76620622004-01-13 10:19:37 +00006158 len += partlen;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00006159
6160 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6161 memtodest(p, partlen, syntax, quotes);
6162
6163 if (*ap && sep) {
Glenn L McGrath76620622004-01-13 10:19:37 +00006164 char *q;
6165
6166 len++;
6167 if (subtype == VSPLUS || subtype == VSLENGTH) {
6168 continue;
6169 }
6170 q = expdest;
Eric Andersencb57d552001-06-28 07:25:16 +00006171 if (sepq)
Glenn L McGrath76620622004-01-13 10:19:37 +00006172 STPUTC(CTLESC, q);
6173 STPUTC(sep, q);
6174 expdest = q;
Eric Andersencb57d552001-06-28 07:25:16 +00006175 }
6176 }
Glenn L McGrath76620622004-01-13 10:19:37 +00006177 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00006178 case '0':
Glenn L McGrath76620622004-01-13 10:19:37 +00006179 case '1':
6180 case '2':
6181 case '3':
6182 case '4':
6183 case '5':
6184 case '6':
6185 case '7':
6186 case '8':
6187 case '9':
Eric Andersencb57d552001-06-28 07:25:16 +00006188 num = atoi(name);
Glenn L McGrath76620622004-01-13 10:19:37 +00006189 if (num < 0 || num > shellparam.nparam)
6190 return -1;
6191 p = num ? shellparam.p[num - 1] : arg0;
6192 goto value;
6193 default:
6194 p = lookupvar(name);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006195 value:
Glenn L McGrath76620622004-01-13 10:19:37 +00006196 if (!p)
6197 return -1;
6198
6199 len = strlen(p);
6200 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6201 memtodest(p, len, syntax, quotes);
6202 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00006203 }
Glenn L McGrath76620622004-01-13 10:19:37 +00006204
6205 if (subtype == VSPLUS || subtype == VSLENGTH)
6206 STADJUST(-len, expdest);
6207 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00006208}
6209
6210
Eric Andersencb57d552001-06-28 07:25:16 +00006211/*
6212 * Record the fact that we have to scan this region of the
6213 * string for IFS characters.
6214 */
Eric Andersenc470f442003-07-28 09:56:35 +00006215static void
6216recordregion(int start, int end, int nulonly)
Eric Andersencb57d552001-06-28 07:25:16 +00006217{
6218 struct ifsregion *ifsp;
6219
6220 if (ifslastp == NULL) {
6221 ifsp = &ifsfirst;
6222 } else {
Denis Vlasenkob012b102007-02-19 22:43:01 +00006223 INT_OFF;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00006224 ifsp = ckmalloc(sizeof(*ifsp));
Eric Andersencb57d552001-06-28 07:25:16 +00006225 ifsp->next = NULL;
6226 ifslastp->next = ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006227 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00006228 }
6229 ifslastp = ifsp;
6230 ifslastp->begoff = start;
6231 ifslastp->endoff = end;
6232 ifslastp->nulonly = nulonly;
6233}
6234
6235
Eric Andersencb57d552001-06-28 07:25:16 +00006236/*
6237 * Break the argument string into pieces based upon IFS and add the
6238 * strings to the argument list. The regions of the string to be
6239 * searched for IFS characters have been stored by recordregion.
6240 */
Eric Andersenc470f442003-07-28 09:56:35 +00006241static void
6242ifsbreakup(char *string, struct arglist *arglist)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006243{
Eric Andersencb57d552001-06-28 07:25:16 +00006244 struct ifsregion *ifsp;
6245 struct strlist *sp;
6246 char *start;
6247 char *p;
6248 char *q;
6249 const char *ifs, *realifs;
6250 int ifsspc;
6251 int nulonly;
6252
Eric Andersencb57d552001-06-28 07:25:16 +00006253 start = string;
Eric Andersencb57d552001-06-28 07:25:16 +00006254 if (ifslastp != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00006255 ifsspc = 0;
6256 nulonly = 0;
6257 realifs = ifsset() ? ifsval() : defifs;
Eric Andersencb57d552001-06-28 07:25:16 +00006258 ifsp = &ifsfirst;
6259 do {
6260 p = string + ifsp->begoff;
6261 nulonly = ifsp->nulonly;
6262 ifs = nulonly ? nullstr : realifs;
6263 ifsspc = 0;
6264 while (p < string + ifsp->endoff) {
6265 q = p;
6266 if (*p == CTLESC)
6267 p++;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006268 if (!strchr(ifs, *p)) {
Eric Andersencb57d552001-06-28 07:25:16 +00006269 p++;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006270 continue;
6271 }
6272 if (!nulonly)
6273 ifsspc = (strchr(defifs, *p) != NULL);
6274 /* Ignore IFS whitespace at start */
6275 if (q == start && ifsspc) {
6276 p++;
6277 start = p;
6278 continue;
6279 }
6280 *q = '\0';
6281 sp = stalloc(sizeof(*sp));
6282 sp->text = start;
6283 *arglist->lastp = sp;
6284 arglist->lastp = &sp->next;
6285 p++;
6286 if (!nulonly) {
6287 for (;;) {
6288 if (p >= string + ifsp->endoff) {
6289 break;
6290 }
6291 q = p;
6292 if (*p == CTLESC)
6293 p++;
6294 if (strchr(ifs, *p) == NULL ) {
6295 p = q;
6296 break;
6297 } else if (strchr(defifs, *p) == NULL) {
6298 if (ifsspc) {
Eric Andersencb57d552001-06-28 07:25:16 +00006299 p++;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006300 ifsspc = 0;
6301 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00006302 p = q;
6303 break;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006304 }
6305 } else
6306 p++;
Eric Andersencb57d552001-06-28 07:25:16 +00006307 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00006308 }
6309 start = p;
6310 } /* while */
6311 ifsp = ifsp->next;
6312 } while (ifsp != NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00006313 if (nulonly)
6314 goto add;
Eric Andersencb57d552001-06-28 07:25:16 +00006315 }
6316
Eric Andersenc470f442003-07-28 09:56:35 +00006317 if (!*start)
6318 return;
6319
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006320 add:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00006321 sp = stalloc(sizeof(*sp));
Eric Andersencb57d552001-06-28 07:25:16 +00006322 sp->text = start;
6323 *arglist->lastp = sp;
6324 arglist->lastp = &sp->next;
6325}
6326
Eric Andersenc470f442003-07-28 09:56:35 +00006327static void
6328ifsfree(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006329{
Eric Andersenc470f442003-07-28 09:56:35 +00006330 struct ifsregion *p;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006331
Denis Vlasenkob012b102007-02-19 22:43:01 +00006332 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00006333 p = ifsfirst.next;
6334 do {
6335 struct ifsregion *ifsp;
6336 ifsp = p->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006337 free(p);
Eric Andersenc470f442003-07-28 09:56:35 +00006338 p = ifsp;
6339 } while (p);
Eric Andersencb57d552001-06-28 07:25:16 +00006340 ifslastp = NULL;
6341 ifsfirst.next = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006342 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00006343}
6344
Eric Andersen90898442003-08-06 11:20:52 +00006345static void expmeta(char *, char *);
6346static struct strlist *expsort(struct strlist *);
6347static struct strlist *msort(struct strlist *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00006348
Eric Andersen90898442003-08-06 11:20:52 +00006349static char *expdir;
Eric Andersencb57d552001-06-28 07:25:16 +00006350
Eric Andersencb57d552001-06-28 07:25:16 +00006351
Eric Andersenc470f442003-07-28 09:56:35 +00006352static void
Eric Andersen90898442003-08-06 11:20:52 +00006353expandmeta(struct strlist *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00006354{
Eric Andersen90898442003-08-06 11:20:52 +00006355 static const char metachars[] = {
6356 '*', '?', '[', 0
6357 };
Eric Andersencb57d552001-06-28 07:25:16 +00006358 /* TODO - EXP_REDIR */
6359
6360 while (str) {
Eric Andersen90898442003-08-06 11:20:52 +00006361 struct strlist **savelastp;
6362 struct strlist *sp;
6363 char *p;
Eric Andersenc470f442003-07-28 09:56:35 +00006364
Eric Andersencb57d552001-06-28 07:25:16 +00006365 if (fflag)
6366 goto nometa;
Eric Andersen90898442003-08-06 11:20:52 +00006367 if (!strpbrk(str->text, metachars))
6368 goto nometa;
6369 savelastp = exparg.lastp;
6370
Denis Vlasenkob012b102007-02-19 22:43:01 +00006371 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00006372 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Eric Andersen90898442003-08-06 11:20:52 +00006373 {
6374 int i = strlen(str->text);
6375 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6376 }
6377
6378 expmeta(expdir, p);
Denis Vlasenkob012b102007-02-19 22:43:01 +00006379 free(expdir);
Eric Andersenc470f442003-07-28 09:56:35 +00006380 if (p != str->text)
Denis Vlasenkob012b102007-02-19 22:43:01 +00006381 free(p);
6382 INT_ON;
Eric Andersen90898442003-08-06 11:20:52 +00006383 if (exparg.lastp == savelastp) {
6384 /*
6385 * no matches
6386 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006387 nometa:
Eric Andersencb57d552001-06-28 07:25:16 +00006388 *exparg.lastp = str;
6389 rmescapes(str->text);
6390 exparg.lastp = &str->next;
Eric Andersen90898442003-08-06 11:20:52 +00006391 } else {
6392 *exparg.lastp = NULL;
6393 *savelastp = sp = expsort(*savelastp);
6394 while (sp->next != NULL)
6395 sp = sp->next;
6396 exparg.lastp = &sp->next;
Eric Andersencb57d552001-06-28 07:25:16 +00006397 }
6398 str = str->next;
6399 }
6400}
6401
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006402
Eric Andersencb57d552001-06-28 07:25:16 +00006403/*
Eric Andersenc470f442003-07-28 09:56:35 +00006404 * Add a file name to the list.
Eric Andersencb57d552001-06-28 07:25:16 +00006405 */
Eric Andersenc470f442003-07-28 09:56:35 +00006406static void
Eric Andersen90898442003-08-06 11:20:52 +00006407addfname(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006408{
Eric Andersencb57d552001-06-28 07:25:16 +00006409 struct strlist *sp;
6410
Denis Vlasenkoa624c112007-02-19 22:45:43 +00006411 sp = stalloc(sizeof(*sp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006412 sp->text = ststrdup(name);
Eric Andersenc470f442003-07-28 09:56:35 +00006413 *exparg.lastp = sp;
6414 exparg.lastp = &sp->next;
Eric Andersencb57d552001-06-28 07:25:16 +00006415}
6416
6417
Eric Andersencb57d552001-06-28 07:25:16 +00006418/*
Eric Andersen90898442003-08-06 11:20:52 +00006419 * Do metacharacter (i.e. *, ?, [...]) expansion.
6420 */
Eric Andersen90898442003-08-06 11:20:52 +00006421static void
6422expmeta(char *enddir, char *name)
6423{
6424 char *p;
6425 const char *cp;
6426 char *start;
6427 char *endname;
6428 int metaflag;
Glenn L McGrath005f83a2003-09-01 08:53:32 +00006429 struct stat statb;
Eric Andersen90898442003-08-06 11:20:52 +00006430 DIR *dirp;
6431 struct dirent *dp;
6432 int atend;
6433 int matchdot;
6434
6435 metaflag = 0;
6436 start = name;
6437 for (p = name; *p; p++) {
6438 if (*p == '*' || *p == '?')
6439 metaflag = 1;
6440 else if (*p == '[') {
6441 char *q = p + 1;
6442 if (*q == '!')
6443 q++;
6444 for (;;) {
6445 if (*q == '\\')
6446 q++;
6447 if (*q == '/' || *q == '\0')
6448 break;
6449 if (*++q == ']') {
6450 metaflag = 1;
6451 break;
6452 }
6453 }
6454 } else if (*p == '\\')
6455 p++;
6456 else if (*p == '/') {
6457 if (metaflag)
6458 goto out;
6459 start = p + 1;
6460 }
6461 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006462 out:
Eric Andersen90898442003-08-06 11:20:52 +00006463 if (metaflag == 0) { /* we've reached the end of the file name */
6464 if (enddir != expdir)
6465 metaflag++;
6466 p = name;
6467 do {
6468 if (*p == '\\')
6469 p++;
6470 *enddir++ = *p;
6471 } while (*p++);
Glenn L McGrath005f83a2003-09-01 08:53:32 +00006472 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
Eric Andersen90898442003-08-06 11:20:52 +00006473 addfname(expdir);
6474 return;
6475 }
6476 endname = p;
6477 if (name < start) {
6478 p = name;
6479 do {
6480 if (*p == '\\')
6481 p++;
6482 *enddir++ = *p++;
6483 } while (p < start);
6484 }
6485 if (enddir == expdir) {
6486 cp = ".";
6487 } else if (enddir == expdir + 1 && *expdir == '/') {
6488 cp = "/";
6489 } else {
6490 cp = expdir;
6491 enddir[-1] = '\0';
6492 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006493 dirp = opendir(cp);
6494 if (dirp == NULL)
Eric Andersen90898442003-08-06 11:20:52 +00006495 return;
6496 if (enddir != expdir)
6497 enddir[-1] = '/';
6498 if (*endname == 0) {
6499 atend = 1;
6500 } else {
6501 atend = 0;
6502 *endname++ = '\0';
6503 }
6504 matchdot = 0;
6505 p = start;
6506 if (*p == '\\')
6507 p++;
6508 if (*p == '.')
6509 matchdot++;
6510 while (! intpending && (dp = readdir(dirp)) != NULL) {
6511 if (dp->d_name[0] == '.' && ! matchdot)
6512 continue;
6513 if (pmatch(start, dp->d_name)) {
6514 if (atend) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00006515 strcpy(enddir, dp->d_name);
Eric Andersen90898442003-08-06 11:20:52 +00006516 addfname(expdir);
6517 } else {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00006518 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
Eric Andersen90898442003-08-06 11:20:52 +00006519 continue;
6520 p[-1] = '/';
6521 expmeta(p, endname);
6522 }
6523 }
6524 }
6525 closedir(dirp);
6526 if (! atend)
6527 endname[-1] = '/';
6528}
6529
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006530
Eric Andersen90898442003-08-06 11:20:52 +00006531/*
6532 * Sort the results of file name expansion. It calculates the number of
6533 * strings to sort and then calls msort (short for merge sort) to do the
6534 * work.
6535 */
Eric Andersen90898442003-08-06 11:20:52 +00006536static struct strlist *
6537expsort(struct strlist *str)
6538{
6539 int len;
6540 struct strlist *sp;
6541
6542 len = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00006543 for (sp = str; sp; sp = sp->next)
Eric Andersen90898442003-08-06 11:20:52 +00006544 len++;
6545 return msort(str, len);
6546}
6547
6548
6549static struct strlist *
6550msort(struct strlist *list, int len)
6551{
6552 struct strlist *p, *q = NULL;
6553 struct strlist **lpp;
6554 int half;
6555 int n;
6556
6557 if (len <= 1)
6558 return list;
6559 half = len >> 1;
6560 p = list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00006561 for (n = half; --n >= 0; ) {
Eric Andersen90898442003-08-06 11:20:52 +00006562 q = p;
6563 p = p->next;
6564 }
6565 q->next = NULL; /* terminate first half of list */
6566 q = msort(list, half); /* sort first half of list */
6567 p = msort(p, len - half); /* sort second half */
6568 lpp = &list;
6569 for (;;) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00006570#if ENABLE_LOCALE_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +00006571 if (strcoll(p->text, q->text) < 0)
6572#else
6573 if (strcmp(p->text, q->text) < 0)
6574#endif
6575 {
6576 *lpp = p;
6577 lpp = &p->next;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006578 p = *lpp;
6579 if (p == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +00006580 *lpp = q;
6581 break;
6582 }
6583 } else {
6584 *lpp = q;
6585 lpp = &q->next;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006586 q = *lpp;
6587 if (q == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +00006588 *lpp = p;
6589 break;
6590 }
6591 }
6592 }
6593 return list;
6594}
6595
6596
6597/*
Eric Andersencb57d552001-06-28 07:25:16 +00006598 * Returns true if the pattern matches the string.
6599 */
Rob Landley88621d72006-08-29 19:41:06 +00006600static int patmatch(char *pattern, const char *string)
Eric Andersen2870d962001-07-02 17:27:21 +00006601{
Eric Andersenc470f442003-07-28 09:56:35 +00006602 return pmatch(preglob(pattern, 0, 0), string);
Eric Andersencb57d552001-06-28 07:25:16 +00006603}
6604
6605
Eric Andersencb57d552001-06-28 07:25:16 +00006606/*
6607 * Remove any CTLESC characters from a string.
6608 */
Eric Andersenc470f442003-07-28 09:56:35 +00006609static char *
6610_rmescapes(char *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00006611{
6612 char *p, *q, *r;
6613 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
Eric Andersenc470f442003-07-28 09:56:35 +00006614 unsigned inquotes;
6615 int notescaped;
6616 int globbing;
Eric Andersencb57d552001-06-28 07:25:16 +00006617
6618 p = strpbrk(str, qchars);
6619 if (!p) {
6620 return str;
6621 }
6622 q = p;
6623 r = str;
6624 if (flag & RMESCAPE_ALLOC) {
6625 size_t len = p - str;
Eric Andersenc470f442003-07-28 09:56:35 +00006626 size_t fulllen = len + strlen(p) + 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006627
Eric Andersenc470f442003-07-28 09:56:35 +00006628 if (flag & RMESCAPE_GROW) {
6629 r = makestrspace(fulllen, expdest);
6630 } else if (flag & RMESCAPE_HEAP) {
6631 r = ckmalloc(fulllen);
6632 } else {
6633 r = stalloc(fulllen);
6634 }
6635 q = r;
Eric Andersencb57d552001-06-28 07:25:16 +00006636 if (len > 0) {
Denis Vlasenko7cfecc42006-12-18 22:32:45 +00006637 q = memcpy(q, str, len) + len;
Eric Andersencb57d552001-06-28 07:25:16 +00006638 }
6639 }
Eric Andersenc470f442003-07-28 09:56:35 +00006640 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
6641 globbing = flag & RMESCAPE_GLOB;
6642 notescaped = globbing;
Eric Andersencb57d552001-06-28 07:25:16 +00006643 while (*p) {
6644 if (*p == CTLQUOTEMARK) {
Eric Andersenc470f442003-07-28 09:56:35 +00006645 inquotes = ~inquotes;
Eric Andersencb57d552001-06-28 07:25:16 +00006646 p++;
Eric Andersenc470f442003-07-28 09:56:35 +00006647 notescaped = globbing;
Eric Andersencb57d552001-06-28 07:25:16 +00006648 continue;
6649 }
Eric Andersenc470f442003-07-28 09:56:35 +00006650 if (*p == '\\') {
6651 /* naked back slash */
6652 notescaped = 0;
6653 goto copy;
6654 }
Eric Andersencb57d552001-06-28 07:25:16 +00006655 if (*p == CTLESC) {
6656 p++;
Eric Andersenc470f442003-07-28 09:56:35 +00006657 if (notescaped && inquotes && *p != '/') {
Eric Andersencb57d552001-06-28 07:25:16 +00006658 *q++ = '\\';
6659 }
6660 }
Eric Andersenc470f442003-07-28 09:56:35 +00006661 notescaped = globbing;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006662 copy:
Eric Andersencb57d552001-06-28 07:25:16 +00006663 *q++ = *p++;
6664 }
6665 *q = '\0';
Eric Andersenc470f442003-07-28 09:56:35 +00006666 if (flag & RMESCAPE_GROW) {
6667 expdest = r;
6668 STADJUST(q - r + 1, expdest);
6669 }
Eric Andersencb57d552001-06-28 07:25:16 +00006670 return r;
6671}
Eric Andersencb57d552001-06-28 07:25:16 +00006672
6673
Eric Andersencb57d552001-06-28 07:25:16 +00006674/*
6675 * See if a pattern matches in a case statement.
6676 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006677static int
Eric Andersenc470f442003-07-28 09:56:35 +00006678casematch(union node *pattern, char *val)
Eric Andersen2870d962001-07-02 17:27:21 +00006679{
Eric Andersencb57d552001-06-28 07:25:16 +00006680 struct stackmark smark;
6681 int result;
Eric Andersencb57d552001-06-28 07:25:16 +00006682
6683 setstackmark(&smark);
6684 argbackq = pattern->narg.backquote;
6685 STARTSTACKSTR(expdest);
6686 ifslastp = NULL;
6687 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
Eric Andersenc470f442003-07-28 09:56:35 +00006688 STACKSTRNUL(expdest);
6689 result = patmatch(stackblock(), val);
Eric Andersencb57d552001-06-28 07:25:16 +00006690 popstackmark(&smark);
6691 return result;
6692}
6693
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006694
Eric Andersencb57d552001-06-28 07:25:16 +00006695/*
6696 * Our own itoa().
6697 */
Eric Andersenc470f442003-07-28 09:56:35 +00006698static int
Eric Andersened9ecf72004-06-22 08:29:45 +00006699cvtnum(arith_t num)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006700{
Eric Andersencb57d552001-06-28 07:25:16 +00006701 int len;
6702
Eric Andersenc470f442003-07-28 09:56:35 +00006703 expdest = makestrspace(32, expdest);
Denis Vlasenko131ae172007-02-18 13:00:19 +00006704#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersened9ecf72004-06-22 08:29:45 +00006705 len = fmtstr(expdest, 32, "%lld", (long long) num);
6706#else
Eric Andersenc470f442003-07-28 09:56:35 +00006707 len = fmtstr(expdest, 32, "%ld", num);
Eric Andersened9ecf72004-06-22 08:29:45 +00006708#endif
Eric Andersenc470f442003-07-28 09:56:35 +00006709 STADJUST(len, expdest);
6710 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00006711}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006712
Denis Vlasenko2da584f2007-02-19 22:44:05 +00006713static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
Eric Andersenc470f442003-07-28 09:56:35 +00006714static void
6715varunset(const char *end, const char *var, const char *umsg, int varflags)
Eric Andersencb57d552001-06-28 07:25:16 +00006716{
Eric Andersenc470f442003-07-28 09:56:35 +00006717 const char *msg;
6718 const char *tail;
6719
6720 tail = nullstr;
6721 msg = "parameter not set";
6722 if (umsg) {
6723 if (*end == CTLENDVAR) {
6724 if (varflags & VSNUL)
6725 tail = " or null";
6726 } else
6727 msg = umsg;
6728 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00006729 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
Eric Andersencb57d552001-06-28 07:25:16 +00006730}
Eric Andersen90898442003-08-06 11:20:52 +00006731
6732
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006733/* ============ input.c
6734 *
Eric Andersen90898442003-08-06 11:20:52 +00006735 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00006736 */
Eric Andersenc470f442003-07-28 09:56:35 +00006737#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00006738
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006739enum {
6740 INPUT_PUSH_FILE = 1,
6741 INPUT_NOFILE_OK = 2,
6742};
Eric Andersencb57d552001-06-28 07:25:16 +00006743
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006744static void
6745popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00006746{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006747 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00006748
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006749 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00006750#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006751 if (sp->ap) {
6752 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
6753 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00006754 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006755 if (sp->string != sp->ap->val) {
6756 free(sp->string);
6757 }
6758 sp->ap->flag &= ~ALIASINUSE;
6759 if (sp->ap->flag & ALIASDEAD) {
6760 unalias(sp->ap->name);
6761 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00006762 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006763#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006764 parsenextc = sp->prevstring;
6765 parsenleft = sp->prevnleft;
6766/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
6767 parsefile->strpush = sp->prev;
6768 if (sp != &(parsefile->basestrpush))
6769 free(sp);
6770 INT_ON;
6771}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006772
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006773static int
6774preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006775{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006776 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00006777 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006778 parsenextc = buf;
6779
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006780 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00006781#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00006782 if (!iflag || parsefile->fd)
6783 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6784 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00006785#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006786 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00006787#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006788 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
6789 if (nr == 0) {
6790 /* Ctrl+C pressed */
6791 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00006792 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006793 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00006794 raise(SIGINT);
6795 return 1;
6796 }
Eric Andersenc470f442003-07-28 09:56:35 +00006797 goto retry;
6798 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006799 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00006800 /* Ctrl+D presend */
6801 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006802 }
Eric Andersencb57d552001-06-28 07:25:16 +00006803 }
6804#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00006805 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00006806#endif
6807
6808 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006809 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
6810 int flags = fcntl(0, F_GETFL, 0);
6811 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00006812 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00006813 if (fcntl(0, F_SETFL, flags) >= 0) {
6814 out2str("sh: turning off NDELAY mode\n");
6815 goto retry;
6816 }
6817 }
6818 }
6819 }
6820 return nr;
6821}
6822
6823/*
6824 * Refill the input buffer and return the next input character:
6825 *
6826 * 1) If a string was pushed back on the input, pop it;
6827 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
6828 * from a string so we can't refill the buffer, return EOF.
6829 * 3) If the is more stuff in this buffer, use it else call read to fill it.
6830 * 4) Process input up to the next newline, deleting nul characters.
6831 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006832static int
Eric Andersenc470f442003-07-28 09:56:35 +00006833preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006834{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006835 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00006836 int more;
6837 char savec;
6838
6839 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00006840#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00006841 if (parsenleft == -1 && parsefile->strpush->ap &&
6842 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00006843 return PEOA;
6844 }
Eric Andersen2870d962001-07-02 17:27:21 +00006845#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006846 popstring();
6847 if (--parsenleft >= 0)
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00006848 return SC2INT(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00006849 }
6850 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
6851 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006852 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00006853
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006854 more = parselleft;
6855 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006856 again:
6857 more = preadfd();
6858 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006859 parselleft = parsenleft = EOF_NLEFT;
6860 return PEOF;
6861 }
6862 }
6863
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006864 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00006865
6866 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006867 for (;;) {
6868 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00006869
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006870 more--;
6871 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00006872
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006873 if (!c)
6874 memmove(q, q + 1, more);
6875 else {
6876 q++;
6877 if (c == '\n') {
6878 parsenleft = q - parsenextc - 1;
6879 break;
6880 }
Eric Andersencb57d552001-06-28 07:25:16 +00006881 }
6882
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006883 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006884 parsenleft = q - parsenextc - 1;
6885 if (parsenleft < 0)
6886 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006887 break;
Eric Andersencb57d552001-06-28 07:25:16 +00006888 }
6889 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006890 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00006891
6892 savec = *q;
6893 *q = '\0';
6894
6895 if (vflag) {
6896 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00006897 }
6898
6899 *q = savec;
6900
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00006901 return SC2INT(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00006902}
6903
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00006904
6905#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
6906
6907#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
6908#define pgetc_macro() pgetc()
6909static int
6910pgetc(void)
6911{
6912 return pgetc_as_macro();
6913}
6914#else
6915#define pgetc_macro() pgetc_as_macro()
6916static int
6917pgetc(void)
6918{
6919 return pgetc_macro();
6920}
6921#endif
6922
6923/*
6924 * Same as pgetc(), but ignores PEOA.
6925 */
6926#if ENABLE_ASH_ALIAS
6927static int
6928pgetc2(void)
6929{
6930 int c;
6931
6932 do {
6933 c = pgetc_macro();
6934 } while (c == PEOA);
6935 return c;
6936}
6937#else
6938static int
6939pgetc2(void)
6940{
6941 return pgetc_macro();
6942}
6943#endif
6944
6945/*
6946 * Read a line from the script.
6947 */
6948static char *
6949pfgets(char *line, int len)
6950{
6951 char *p = line;
6952 int nleft = len;
6953 int c;
6954
6955 while (--nleft > 0) {
6956 c = pgetc2();
6957 if (c == PEOF) {
6958 if (p == line)
6959 return NULL;
6960 break;
6961 }
6962 *p++ = c;
6963 if (c == '\n')
6964 break;
6965 }
6966 *p = '\0';
6967 return line;
6968}
6969
Eric Andersenc470f442003-07-28 09:56:35 +00006970/*
6971 * Undo the last call to pgetc. Only one character may be pushed back.
6972 * PEOF may be pushed back.
6973 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006974static void
Eric Andersenc470f442003-07-28 09:56:35 +00006975pungetc(void)
6976{
6977 parsenleft++;
6978 parsenextc--;
6979}
Eric Andersencb57d552001-06-28 07:25:16 +00006980
6981/*
6982 * Push a string back onto the input at this current parsefile level.
6983 * We handle aliases this way.
6984 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006985static void
Eric Andersenc470f442003-07-28 09:56:35 +00006986pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00006987{
Eric Andersencb57d552001-06-28 07:25:16 +00006988 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00006989 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00006990
Eric Andersenc470f442003-07-28 09:56:35 +00006991 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00006992 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00006993/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
6994 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00006995 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00006996 sp->prev = parsefile->strpush;
6997 parsefile->strpush = sp;
6998 } else
6999 sp = parsefile->strpush = &(parsefile->basestrpush);
7000 sp->prevstring = parsenextc;
7001 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00007002#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00007003 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00007004 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00007005 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00007006 sp->string = s;
7007 }
Eric Andersen2870d962001-07-02 17:27:21 +00007008#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007009 parsenextc = s;
7010 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007011 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007012}
7013
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007014/*
7015 * To handle the "." command, a stack of input files is used. Pushfile
7016 * adds a new entry to the stack and popfile restores the previous level.
7017 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007018static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007019pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00007020{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007021 struct parsefile *pf;
7022
7023 parsefile->nleft = parsenleft;
7024 parsefile->lleft = parselleft;
7025 parsefile->nextc = parsenextc;
7026 parsefile->linno = plinno;
7027 pf = ckmalloc(sizeof(*pf));
7028 pf->prev = parsefile;
7029 pf->fd = -1;
7030 pf->strpush = NULL;
7031 pf->basestrpush.prev = NULL;
7032 parsefile = pf;
7033}
7034
7035static void
7036popfile(void)
7037{
7038 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00007039
Denis Vlasenkob012b102007-02-19 22:43:01 +00007040 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007041 if (pf->fd >= 0)
7042 close(pf->fd);
7043 if (pf->buf)
7044 free(pf->buf);
7045 while (pf->strpush)
7046 popstring();
7047 parsefile = pf->prev;
7048 free(pf);
7049 parsenleft = parsefile->nleft;
7050 parselleft = parsefile->lleft;
7051 parsenextc = parsefile->nextc;
7052 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007053 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007054}
7055
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007056/*
7057 * Return to top level.
7058 */
7059static void
7060popallfiles(void)
7061{
7062 while (parsefile != &basepf)
7063 popfile();
7064}
7065
7066/*
7067 * Close the file(s) that the shell is reading commands from. Called
7068 * after a fork is done.
7069 */
7070static void
7071closescript(void)
7072{
7073 popallfiles();
7074 if (parsefile->fd > 0) {
7075 close(parsefile->fd);
7076 parsefile->fd = 0;
7077 }
7078}
7079
7080/*
7081 * Like setinputfile, but takes an open file descriptor. Call this with
7082 * interrupts off.
7083 */
7084static void
7085setinputfd(int fd, int push)
7086{
7087 fcntl(fd, F_SETFD, FD_CLOEXEC);
7088 if (push) {
7089 pushfile();
7090 parsefile->buf = 0;
7091 }
7092 parsefile->fd = fd;
7093 if (parsefile->buf == NULL)
7094 parsefile->buf = ckmalloc(IBUFSIZ);
7095 parselleft = parsenleft = 0;
7096 plinno = 1;
7097}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007098
Eric Andersenc470f442003-07-28 09:56:35 +00007099/*
7100 * Set the input to take input from a file. If push is set, push the
7101 * old input onto the stack first.
7102 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007103static int
7104setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00007105{
7106 int fd;
7107 int fd2;
7108
Denis Vlasenkob012b102007-02-19 22:43:01 +00007109 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007110 fd = open(fname, O_RDONLY);
7111 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007112 if (flags & INPUT_NOFILE_OK)
7113 goto out;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007114 ash_msg_and_raise_error("Can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007115 }
Eric Andersenc470f442003-07-28 09:56:35 +00007116 if (fd < 10) {
7117 fd2 = copyfd(fd, 10);
7118 close(fd);
7119 if (fd2 < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007120 ash_msg_and_raise_error("Out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00007121 fd = fd2;
7122 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007123 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007124 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007125 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007126 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00007127}
7128
Eric Andersencb57d552001-06-28 07:25:16 +00007129/*
7130 * Like setinputfile, but takes input from a string.
7131 */
Eric Andersenc470f442003-07-28 09:56:35 +00007132static void
7133setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00007134{
Denis Vlasenkob012b102007-02-19 22:43:01 +00007135 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007136 pushfile();
7137 parsenextc = string;
7138 parsenleft = strlen(string);
7139 parsefile->buf = NULL;
7140 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007141 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007142}
7143
7144
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007145/* jobs.c */
Eric Andersenc470f442003-07-28 09:56:35 +00007146
7147/* mode flags for set_curjob */
7148#define CUR_DELETE 2
7149#define CUR_RUNNING 1
7150#define CUR_STOPPED 0
7151
7152/* mode flags for dowait */
7153#define DOWAIT_NORMAL 0
7154#define DOWAIT_BLOCK 1
7155
Eric Andersenc470f442003-07-28 09:56:35 +00007156#if JOBS
7157/* pgrp of shell on invocation */
7158static int initialpgrp;
7159static int ttyfd = -1;
7160#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007161/* array of jobs */
7162static struct job *jobtab;
7163/* size of array */
7164static unsigned njobs;
Eric Andersenc470f442003-07-28 09:56:35 +00007165/* current job */
7166static struct job *curjob;
7167/* number of presumed living untracked jobs */
7168static int jobless;
7169
Eric Andersenc470f442003-07-28 09:56:35 +00007170static void
7171set_curjob(struct job *jp, unsigned mode)
7172{
7173 struct job *jp1;
7174 struct job **jpp, **curp;
7175
7176 /* first remove from list */
7177 jpp = curp = &curjob;
7178 do {
7179 jp1 = *jpp;
7180 if (jp1 == jp)
7181 break;
7182 jpp = &jp1->prev_job;
7183 } while (1);
7184 *jpp = jp1->prev_job;
7185
7186 /* Then re-insert in correct position */
7187 jpp = curp;
7188 switch (mode) {
7189 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007190#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007191 abort();
7192#endif
7193 case CUR_DELETE:
7194 /* job being deleted */
7195 break;
7196 case CUR_RUNNING:
7197 /* newly created job or backgrounded job,
7198 put after all stopped jobs. */
7199 do {
7200 jp1 = *jpp;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007201#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007202 if (!jp1 || jp1->state != JOBSTOPPED)
7203#endif
7204 break;
7205 jpp = &jp1->prev_job;
7206 } while (1);
7207 /* FALLTHROUGH */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007208#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007209 case CUR_STOPPED:
7210#endif
7211 /* newly stopped job - becomes curjob */
7212 jp->prev_job = *jpp;
7213 *jpp = jp;
7214 break;
7215 }
7216}
7217
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007218#if JOBS || DEBUG
7219static int
7220jobno(const struct job *jp)
7221{
7222 return jp - jobtab + 1;
7223}
7224#endif
7225
7226/*
7227 * Convert a job name to a job structure.
7228 */
7229static struct job *
7230getjob(const char *name, int getctl)
7231{
7232 struct job *jp;
7233 struct job *found;
7234 const char *err_msg = "No such job: %s";
7235 unsigned num;
7236 int c;
7237 const char *p;
7238 char *(*match)(const char *, const char *);
7239
7240 jp = curjob;
7241 p = name;
7242 if (!p)
7243 goto currentjob;
7244
7245 if (*p != '%')
7246 goto err;
7247
7248 c = *++p;
7249 if (!c)
7250 goto currentjob;
7251
7252 if (!p[1]) {
7253 if (c == '+' || c == '%') {
7254 currentjob:
7255 err_msg = "No current job";
7256 goto check;
7257 }
7258 if (c == '-') {
7259 if (jp)
7260 jp = jp->prev_job;
7261 err_msg = "No previous job";
7262 check:
7263 if (!jp)
7264 goto err;
7265 goto gotit;
7266 }
7267 }
7268
7269 if (is_number(p)) {
7270 num = atoi(p);
7271 if (num < njobs) {
7272 jp = jobtab + num - 1;
7273 if (jp->used)
7274 goto gotit;
7275 goto err;
7276 }
7277 }
7278
7279 match = prefix;
7280 if (*p == '?') {
7281 match = strstr;
7282 p++;
7283 }
7284
7285 found = 0;
7286 while (1) {
7287 if (!jp)
7288 goto err;
7289 if (match(jp->ps[0].cmd, p)) {
7290 if (found)
7291 goto err;
7292 found = jp;
7293 err_msg = "%s: ambiguous";
7294 }
7295 jp = jp->prev_job;
7296 }
7297
7298 gotit:
Eric Andersenc470f442003-07-28 09:56:35 +00007299#if JOBS
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007300 err_msg = "job %s not created under job control";
7301 if (getctl && jp->jobctl == 0)
7302 goto err;
7303#endif
7304 return jp;
7305 err:
7306 ash_msg_and_raise_error(err_msg, name);
7307}
7308
7309/*
7310 * Mark a job structure as unused.
7311 */
7312static void
7313freejob(struct job *jp)
7314{
7315 struct procstat *ps;
7316 int i;
7317
7318 INT_OFF;
7319 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
7320 if (ps->cmd != nullstr)
7321 free(ps->cmd);
7322 }
7323 if (jp->ps != &jp->ps0)
7324 free(jp->ps);
7325 jp->used = 0;
7326 set_curjob(jp, CUR_DELETE);
7327 INT_ON;
7328}
7329
7330#if JOBS
7331static void
7332xtcsetpgrp(int fd, pid_t pgrp)
7333{
7334 if (tcsetpgrp(fd, pgrp))
7335 ash_msg_and_raise_error("Cannot set tty process group (%m)");
7336}
7337
Eric Andersencb57d552001-06-28 07:25:16 +00007338/*
7339 * Turn job control on and off.
7340 *
7341 * Note: This code assumes that the third arg to ioctl is a character
7342 * pointer, which is true on Berkeley systems but not System V. Since
7343 * System V doesn't have job control yet, this isn't a problem now.
Eric Andersenc470f442003-07-28 09:56:35 +00007344 *
7345 * Called with interrupts off.
Eric Andersencb57d552001-06-28 07:25:16 +00007346 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007347static void
Eric Andersenc470f442003-07-28 09:56:35 +00007348setjobctl(int on)
Eric Andersencb57d552001-06-28 07:25:16 +00007349{
Eric Andersenc470f442003-07-28 09:56:35 +00007350 int fd;
7351 int pgrp;
7352
7353 if (on == jobctl || rootshell == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00007354 return;
Eric Andersenc470f442003-07-28 09:56:35 +00007355 if (on) {
7356 int ofd;
7357 ofd = fd = open(_PATH_TTY, O_RDWR);
7358 if (fd < 0) {
Denis Vlasenko2f0c0d02007-01-21 00:41:04 +00007359 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
7360 * That sometimes helps to acquire controlling tty.
7361 * Obviously, a workaround for bugs when someone
7362 * failed to provide a controlling tty to bash! :) */
Eric Andersenc470f442003-07-28 09:56:35 +00007363 fd += 3;
7364 while (!isatty(fd) && --fd >= 0)
7365 ;
7366 }
7367 fd = fcntl(fd, F_DUPFD, 10);
7368 close(ofd);
7369 if (fd < 0)
7370 goto out;
7371 fcntl(fd, F_SETFD, FD_CLOEXEC);
7372 do { /* while we are in the background */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007373 pgrp = tcgetpgrp(fd);
7374 if (pgrp < 0) {
7375 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007376 ash_msg("can't access tty; job control turned off");
Eric Andersenc470f442003-07-28 09:56:35 +00007377 mflag = on = 0;
7378 goto close;
Eric Andersencb57d552001-06-28 07:25:16 +00007379 }
Eric Andersenc470f442003-07-28 09:56:35 +00007380 if (pgrp == getpgrp())
Glenn L McGrath7040ecc2003-01-06 16:27:07 +00007381 break;
7382 killpg(0, SIGTTIN);
7383 } while (1);
Eric Andersenc470f442003-07-28 09:56:35 +00007384 initialpgrp = pgrp;
7385
Eric Andersencb57d552001-06-28 07:25:16 +00007386 setsignal(SIGTSTP);
7387 setsignal(SIGTTOU);
7388 setsignal(SIGTTIN);
Eric Andersenc470f442003-07-28 09:56:35 +00007389 pgrp = rootpid;
7390 setpgid(0, pgrp);
7391 xtcsetpgrp(fd, pgrp);
7392 } else {
7393 /* turning job control off */
7394 fd = ttyfd;
7395 pgrp = initialpgrp;
7396 xtcsetpgrp(fd, pgrp);
7397 setpgid(0, pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00007398 setsignal(SIGTSTP);
7399 setsignal(SIGTTOU);
7400 setsignal(SIGTTIN);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007401 close:
Eric Andersenc470f442003-07-28 09:56:35 +00007402 close(fd);
7403 fd = -1;
Eric Andersencb57d552001-06-28 07:25:16 +00007404 }
Eric Andersenc470f442003-07-28 09:56:35 +00007405 ttyfd = fd;
7406 jobctl = on;
Eric Andersencb57d552001-06-28 07:25:16 +00007407}
Eric Andersencb57d552001-06-28 07:25:16 +00007408
Eric Andersenc470f442003-07-28 09:56:35 +00007409static int
Eric Andersen90898442003-08-06 11:20:52 +00007410killcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007411{
7412 int signo = -1;
7413 int list = 0;
7414 int i;
7415 pid_t pid;
7416 struct job *jp;
7417
7418 if (argc <= 1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007419 usage:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007420 ash_msg_and_raise_error(
Eric Andersenc470f442003-07-28 09:56:35 +00007421"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
7422"kill -l [exitstatus]"
7423 );
Eric Andersencb57d552001-06-28 07:25:16 +00007424 }
7425
Eric Andersenc470f442003-07-28 09:56:35 +00007426 if (**++argv == '-') {
Rob Landleyc9c1a412006-07-12 19:17:55 +00007427 signo = get_signum(*argv + 1);
Eric Andersencb57d552001-06-28 07:25:16 +00007428 if (signo < 0) {
7429 int c;
7430
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007431 while ((c = nextopt("ls:")) != '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00007432 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +00007433 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007434#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007435 abort();
7436#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007437 case 'l':
7438 list = 1;
7439 break;
7440 case 's':
Rob Landleyc9c1a412006-07-12 19:17:55 +00007441 signo = get_signum(optionarg);
Eric Andersencb57d552001-06-28 07:25:16 +00007442 if (signo < 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007443 ash_msg_and_raise_error(
Eric Andersenc470f442003-07-28 09:56:35 +00007444 "invalid signal number or name: %s",
7445 optionarg
7446 );
Eric Andersencb57d552001-06-28 07:25:16 +00007447 }
Eric Andersen2870d962001-07-02 17:27:21 +00007448 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007449 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007450 }
Eric Andersenc470f442003-07-28 09:56:35 +00007451 argv = argptr;
Eric Andersencb57d552001-06-28 07:25:16 +00007452 } else
Eric Andersenc470f442003-07-28 09:56:35 +00007453 argv++;
Eric Andersencb57d552001-06-28 07:25:16 +00007454 }
7455
7456 if (!list && signo < 0)
7457 signo = SIGTERM;
7458
Eric Andersenc470f442003-07-28 09:56:35 +00007459 if ((signo < 0 || !*argv) ^ list) {
Eric Andersencb57d552001-06-28 07:25:16 +00007460 goto usage;
7461 }
7462
7463 if (list) {
Eric Andersen34506362001-08-02 05:02:46 +00007464 const char *name;
7465
Eric Andersenc470f442003-07-28 09:56:35 +00007466 if (!*argv) {
Eric Andersencb57d552001-06-28 07:25:16 +00007467 for (i = 1; i < NSIG; i++) {
Rob Landleyc9c1a412006-07-12 19:17:55 +00007468 name = get_signame(i);
7469 if (isdigit(*name))
Eric Andersenc470f442003-07-28 09:56:35 +00007470 out1fmt(snlfmt, name);
Eric Andersencb57d552001-06-28 07:25:16 +00007471 }
7472 return 0;
7473 }
Rob Landleyc9c1a412006-07-12 19:17:55 +00007474 name = get_signame(signo);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007475 if (!isdigit(*name))
Denis Vlasenkob012b102007-02-19 22:43:01 +00007476 ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007477 out1fmt(snlfmt, name);
Eric Andersencb57d552001-06-28 07:25:16 +00007478 return 0;
7479 }
7480
Eric Andersenc470f442003-07-28 09:56:35 +00007481 i = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00007482 do {
Eric Andersenc470f442003-07-28 09:56:35 +00007483 if (**argv == '%') {
7484 jp = getjob(*argv, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00007485 pid = -jp->ps[0].pid;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00007486 } else {
7487 pid = **argv == '-' ?
7488 -number(*argv + 1) : number(*argv);
7489 }
Eric Andersenc470f442003-07-28 09:56:35 +00007490 if (kill(pid, signo) != 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007491 ash_msg("(%d) - %m", pid);
Eric Andersenc470f442003-07-28 09:56:35 +00007492 i = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00007493 }
Eric Andersenc470f442003-07-28 09:56:35 +00007494 } while (*++argv);
7495
7496 return i;
7497}
Eric Andersenc470f442003-07-28 09:56:35 +00007498
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007499static void
7500showpipe(struct job *jp, FILE *out)
Eric Andersenc470f442003-07-28 09:56:35 +00007501{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007502 struct procstat *sp;
7503 struct procstat *spend;
Eric Andersencb57d552001-06-28 07:25:16 +00007504
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007505 spend = jp->ps + jp->nprocs;
7506 for (sp = jp->ps + 1; sp < spend; sp++)
7507 fprintf(out, " | %s", sp->cmd);
7508 outcslow('\n', out);
7509 flush_stdout_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00007510}
7511
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007512
Eric Andersenc470f442003-07-28 09:56:35 +00007513static int
7514restartjob(struct job *jp, int mode)
7515{
7516 struct procstat *ps;
7517 int i;
7518 int status;
7519 pid_t pgid;
7520
Denis Vlasenkob012b102007-02-19 22:43:01 +00007521 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007522 if (jp->state == JOBDONE)
7523 goto out;
7524 jp->state = JOBRUNNING;
7525 pgid = jp->ps->pid;
7526 if (mode == FORK_FG)
7527 xtcsetpgrp(ttyfd, pgid);
7528 killpg(pgid, SIGCONT);
7529 ps = jp->ps;
7530 i = jp->nprocs;
7531 do {
7532 if (WIFSTOPPED(ps->status)) {
7533 ps->status = -1;
7534 }
7535 } while (ps++, --i);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007536 out:
Eric Andersenc470f442003-07-28 09:56:35 +00007537 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007538 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007539 return status;
7540}
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007541
7542static int
7543fg_bgcmd(int argc, char **argv)
7544{
7545 struct job *jp;
7546 FILE *out;
7547 int mode;
7548 int retval;
7549
7550 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
7551 nextopt(nullstr);
7552 argv = argptr;
7553 out = stdout;
7554 do {
7555 jp = getjob(*argv, 1);
7556 if (mode == FORK_BG) {
7557 set_curjob(jp, CUR_RUNNING);
7558 fprintf(out, "[%d] ", jobno(jp));
7559 }
7560 outstr(jp->ps->cmd, out);
7561 showpipe(jp, out);
7562 retval = restartjob(jp, mode);
7563 } while (*argv && *++argv);
7564 return retval;
7565}
Eric Andersenc470f442003-07-28 09:56:35 +00007566#endif
7567
7568static int
7569sprint_status(char *s, int status, int sigonly)
7570{
7571 int col;
7572 int st;
7573
7574 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00007575 if (!WIFEXITED(status)) {
Eric Andersenc470f442003-07-28 09:56:35 +00007576#if JOBS
Glenn L McGratha3822de2003-09-15 14:42:39 +00007577 if (WIFSTOPPED(status))
7578 st = WSTOPSIG(status);
Eric Andersena48b0a32003-10-22 10:56:47 +00007579 else
Eric Andersenc470f442003-07-28 09:56:35 +00007580#endif
Eric Andersena48b0a32003-10-22 10:56:47 +00007581 st = WTERMSIG(status);
Eric Andersenc470f442003-07-28 09:56:35 +00007582 if (sigonly) {
Glenn L McGrath4ddddd12003-11-25 20:45:38 +00007583 if (st == SIGINT || st == SIGPIPE)
Eric Andersena48b0a32003-10-22 10:56:47 +00007584 goto out;
7585#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007586 if (WIFSTOPPED(status))
7587 goto out;
Eric Andersena48b0a32003-10-22 10:56:47 +00007588#endif
Eric Andersenc470f442003-07-28 09:56:35 +00007589 }
7590 st &= 0x7f;
Glenn L McGrath5c2c8ec2003-11-14 21:01:26 +00007591 col = fmtstr(s, 32, strsignal(st));
Eric Andersenc470f442003-07-28 09:56:35 +00007592 if (WCOREDUMP(status)) {
7593 col += fmtstr(s + col, 16, " (core dumped)");
7594 }
7595 } else if (!sigonly) {
Eric Andersena48b0a32003-10-22 10:56:47 +00007596 st = WEXITSTATUS(status);
Eric Andersenc470f442003-07-28 09:56:35 +00007597 if (st)
7598 col = fmtstr(s, 16, "Done(%d)", st);
7599 else
7600 col = fmtstr(s, 16, "Done");
7601 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007602 out:
Eric Andersenc470f442003-07-28 09:56:35 +00007603 return col;
7604}
7605
Eric Andersen62483552001-07-10 06:09:16 +00007606/*
7607 * Do a wait system call. If job control is compiled in, we accept
7608 * stopped processes. If block is zero, we return a value of zero
7609 * rather than blocking.
7610 *
7611 * System V doesn't have a non-blocking wait system call. It does
7612 * have a SIGCLD signal that is sent to a process when one of it's
7613 * children dies. The obvious way to use SIGCLD would be to install
7614 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
7615 * was received, and have waitproc bump another counter when it got
7616 * the status of a process. Waitproc would then know that a wait
7617 * system call would not block if the two counters were different.
7618 * This approach doesn't work because if a process has children that
7619 * have not been waited for, System V will send it a SIGCLD when it
7620 * installs a signal handler for SIGCLD. What this means is that when
7621 * a child exits, the shell will be sent SIGCLD signals continuously
7622 * until is runs out of stack space, unless it does a wait call before
7623 * restoring the signal handler. The code below takes advantage of
7624 * this (mis)feature by installing a signal handler for SIGCLD and
7625 * then checking to see whether it was called. If there are any
7626 * children to be waited for, it will be.
7627 *
Eric Andersenc470f442003-07-28 09:56:35 +00007628 * If neither SYSV nor BSD is defined, we don't implement nonblocking
7629 * waits at all. In this case, the user will not be informed when
7630 * a background process until the next time she runs a real program
7631 * (as opposed to running a builtin command or just typing return),
7632 * and the jobs command may give out of date information.
Eric Andersen62483552001-07-10 06:09:16 +00007633 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007634static int
7635waitproc(int block, int *status)
Eric Andersen62483552001-07-10 06:09:16 +00007636{
Eric Andersenc470f442003-07-28 09:56:35 +00007637 int flags = 0;
Eric Andersen62483552001-07-10 06:09:16 +00007638
Eric Andersenc470f442003-07-28 09:56:35 +00007639#if JOBS
Eric Andersen62483552001-07-10 06:09:16 +00007640 if (jobctl)
7641 flags |= WUNTRACED;
7642#endif
7643 if (block == 0)
7644 flags |= WNOHANG;
Eric Andersenc470f442003-07-28 09:56:35 +00007645 return wait3(status, flags, (struct rusage *)NULL);
Eric Andersen62483552001-07-10 06:09:16 +00007646}
7647
Eric Andersenc470f442003-07-28 09:56:35 +00007648/*
7649 * Wait for a process to terminate.
7650 */
Eric Andersenc470f442003-07-28 09:56:35 +00007651static int
7652dowait(int block, struct job *job)
Eric Andersencb57d552001-06-28 07:25:16 +00007653{
7654 int pid;
7655 int status;
Eric Andersencb57d552001-06-28 07:25:16 +00007656 struct job *jp;
7657 struct job *thisjob;
Eric Andersenc470f442003-07-28 09:56:35 +00007658 int state;
Eric Andersencb57d552001-06-28 07:25:16 +00007659
7660 TRACE(("dowait(%d) called\n", block));
Eric Andersenc470f442003-07-28 09:56:35 +00007661 pid = waitproc(block, &status);
7662 TRACE(("wait returns pid %d, status=%d\n", pid, status));
Eric Andersencb57d552001-06-28 07:25:16 +00007663 if (pid <= 0)
7664 return pid;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007665 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007666 thisjob = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00007667 for (jp = curjob; jp; jp = jp->prev_job) {
7668 struct procstat *sp;
7669 struct procstat *spend;
7670 if (jp->state == JOBDONE)
7671 continue;
7672 state = JOBDONE;
7673 spend = jp->ps + jp->nprocs;
7674 sp = jp->ps;
7675 do {
7676 if (sp->pid == pid) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007677 TRACE(("Job %d: changing status of proc %d "
7678 "from 0x%x to 0x%x\n",
7679 jobno(jp), pid, sp->status, status));
Eric Andersenc470f442003-07-28 09:56:35 +00007680 sp->status = status;
7681 thisjob = jp;
Eric Andersencb57d552001-06-28 07:25:16 +00007682 }
Eric Andersenc470f442003-07-28 09:56:35 +00007683 if (sp->status == -1)
7684 state = JOBRUNNING;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007685#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007686 if (state == JOBRUNNING)
7687 continue;
7688 if (WIFSTOPPED(sp->status)) {
7689 jp->stopstatus = sp->status;
7690 state = JOBSTOPPED;
7691 }
Eric Andersencb57d552001-06-28 07:25:16 +00007692#endif
Eric Andersenc470f442003-07-28 09:56:35 +00007693 } while (++sp < spend);
7694 if (thisjob)
7695 goto gotjob;
7696 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007697#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007698 if (!WIFSTOPPED(status))
7699#endif
7700
7701 jobless--;
7702 goto out;
7703
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007704 gotjob:
Eric Andersenc470f442003-07-28 09:56:35 +00007705 if (state != JOBRUNNING) {
7706 thisjob->changed = 1;
7707
7708 if (thisjob->state != state) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007709 TRACE(("Job %d: changing state from %d to %d\n",
7710 jobno(thisjob), thisjob->state, state));
Eric Andersenc470f442003-07-28 09:56:35 +00007711 thisjob->state = state;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007712#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007713 if (state == JOBSTOPPED) {
7714 set_curjob(thisjob, CUR_STOPPED);
Eric Andersencb57d552001-06-28 07:25:16 +00007715 }
Eric Andersenc470f442003-07-28 09:56:35 +00007716#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007717 }
7718 }
Eric Andersencb57d552001-06-28 07:25:16 +00007719
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007720 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007721 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007722
7723 if (thisjob && thisjob == job) {
7724 char s[48 + 1];
7725 int len;
7726
7727 len = sprint_status(s, status, 1);
7728 if (len) {
7729 s[len] = '\n';
7730 s[len + 1] = 0;
7731 out2str(s);
Eric Andersencb57d552001-06-28 07:25:16 +00007732 }
Eric Andersencb57d552001-06-28 07:25:16 +00007733 }
7734 return pid;
7735}
7736
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007737#if JOBS
7738static void
7739showjob(FILE *out, struct job *jp, int mode)
7740{
7741 struct procstat *ps;
7742 struct procstat *psend;
7743 int col;
7744 int indent;
7745 char s[80];
7746
7747 ps = jp->ps;
7748
7749 if (mode & SHOW_PGID) {
7750 /* just output process (group) id of pipeline */
7751 fprintf(out, "%d\n", ps->pid);
7752 return;
7753 }
7754
7755 col = fmtstr(s, 16, "[%d] ", jobno(jp));
7756 indent = col;
7757
7758 if (jp == curjob)
7759 s[col - 2] = '+';
7760 else if (curjob && jp == curjob->prev_job)
7761 s[col - 2] = '-';
7762
7763 if (mode & SHOW_PID)
7764 col += fmtstr(s + col, 16, "%d ", ps->pid);
7765
7766 psend = ps + jp->nprocs;
7767
7768 if (jp->state == JOBRUNNING) {
7769 strcpy(s + col, "Running");
7770 col += sizeof("Running") - 1;
7771 } else {
7772 int status = psend[-1].status;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007773 if (jp->state == JOBSTOPPED)
7774 status = jp->stopstatus;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00007775 col += sprint_status(s + col, status, 0);
7776 }
7777
7778 goto start;
7779
7780 do {
7781 /* for each process */
7782 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
7783 start:
7784 fprintf(out, "%s%*c%s",
7785 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
7786 );
7787 if (!(mode & SHOW_PID)) {
7788 showpipe(jp, out);
7789 break;
7790 }
7791 if (++ps == psend) {
7792 outcslow('\n', out);
7793 break;
7794 }
7795 } while (1);
7796
7797 jp->changed = 0;
7798
7799 if (jp->state == JOBDONE) {
7800 TRACE(("showjob: freeing job %d\n", jobno(jp)));
7801 freejob(jp);
7802 }
7803}
7804
7805static int
7806jobscmd(int argc, char **argv)
7807{
7808 int mode, m;
7809 FILE *out;
7810
7811 mode = 0;
7812 while ((m = nextopt("lp"))) {
7813 if (m == 'l')
7814 mode = SHOW_PID;
7815 else
7816 mode = SHOW_PGID;
7817 }
7818
7819 out = stdout;
7820 argv = argptr;
7821 if (*argv) {
7822 do
7823 showjob(out, getjob(*argv,0), mode);
7824 while (*++argv);
7825 } else
7826 showjobs(out, mode);
7827
7828 return 0;
7829}
7830
7831/*
7832 * Print a list of jobs. If "change" is nonzero, only print jobs whose
7833 * statuses have changed since the last call to showjobs.
7834 */
7835static void
7836showjobs(FILE *out, int mode)
7837{
7838 struct job *jp;
7839
7840 TRACE(("showjobs(%x) called\n", mode));
7841
7842 /* If not even one one job changed, there is nothing to do */
7843 while (dowait(DOWAIT_NORMAL, NULL) > 0)
7844 continue;
7845
7846 for (jp = curjob; jp; jp = jp->prev_job) {
7847 if (!(mode & SHOW_CHANGED) || jp->changed)
7848 showjob(out, jp, mode);
7849 }
7850}
7851#endif /* JOBS */
7852
7853static int
7854getstatus(struct job *job)
7855{
7856 int status;
7857 int retval;
7858
7859 status = job->ps[job->nprocs - 1].status;
7860 retval = WEXITSTATUS(status);
7861 if (!WIFEXITED(status)) {
7862#if JOBS
7863 retval = WSTOPSIG(status);
7864 if (!WIFSTOPPED(status))
7865#endif
7866 {
7867 /* XXX: limits number of signals */
7868 retval = WTERMSIG(status);
7869#if JOBS
7870 if (retval == SIGINT)
7871 job->sigint = 1;
7872#endif
7873 }
7874 retval += 128;
7875 }
7876 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
7877 jobno(job), job->nprocs, status, retval));
7878 return retval;
7879}
7880
7881static int
7882waitcmd(int argc, char **argv)
7883{
7884 struct job *job;
7885 int retval;
7886 struct job *jp;
7887
7888 EXSIGON;
7889
7890 nextopt(nullstr);
7891 retval = 0;
7892
7893 argv = argptr;
7894 if (!*argv) {
7895 /* wait for all jobs */
7896 for (;;) {
7897 jp = curjob;
7898 while (1) {
7899 if (!jp) {
7900 /* no running procs */
7901 goto out;
7902 }
7903 if (jp->state == JOBRUNNING)
7904 break;
7905 jp->waited = 1;
7906 jp = jp->prev_job;
7907 }
7908 dowait(DOWAIT_BLOCK, 0);
7909 }
7910 }
7911
7912 retval = 127;
7913 do {
7914 if (**argv != '%') {
7915 pid_t pid = number(*argv);
7916 job = curjob;
7917 goto start;
7918 do {
7919 if (job->ps[job->nprocs - 1].pid == pid)
7920 break;
7921 job = job->prev_job;
7922 start:
7923 if (!job)
7924 goto repeat;
7925 } while (1);
7926 } else
7927 job = getjob(*argv, 0);
7928 /* loop until process terminated or stopped */
7929 while (job->state == JOBRUNNING)
7930 dowait(DOWAIT_BLOCK, 0);
7931 job->waited = 1;
7932 retval = getstatus(job);
7933 repeat:
7934 ;
7935 } while (*++argv);
7936
7937 out:
7938 return retval;
7939}
7940
7941static struct job *
7942growjobtab(void)
7943{
7944 size_t len;
7945 ptrdiff_t offset;
7946 struct job *jp, *jq;
7947
7948 len = njobs * sizeof(*jp);
7949 jq = jobtab;
7950 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
7951
7952 offset = (char *)jp - (char *)jq;
7953 if (offset) {
7954 /* Relocate pointers */
7955 size_t l = len;
7956
7957 jq = (struct job *)((char *)jq + l);
7958 while (l) {
7959 l -= sizeof(*jp);
7960 jq--;
7961#define joff(p) ((struct job *)((char *)(p) + l))
7962#define jmove(p) (p) = (void *)((char *)(p) + offset)
7963 if (xlikely(joff(jp)->ps == &jq->ps0))
7964 jmove(joff(jp)->ps);
7965 if (joff(jp)->prev_job)
7966 jmove(joff(jp)->prev_job);
7967 }
7968 if (curjob)
7969 jmove(curjob);
7970#undef joff
7971#undef jmove
7972 }
7973
7974 njobs += 4;
7975 jobtab = jp;
7976 jp = (struct job *)((char *)jp + len);
7977 jq = jp + 3;
7978 do {
7979 jq->used = 0;
7980 } while (--jq >= jp);
7981 return jp;
7982}
7983
7984/*
7985 * Return a new job structure.
7986 * Called with interrupts off.
7987 */
7988static struct job *
7989makejob(union node *node, int nprocs)
7990{
7991 int i;
7992 struct job *jp;
7993
7994 for (i = njobs, jp = jobtab; ; jp++) {
7995 if (--i < 0) {
7996 jp = growjobtab();
7997 break;
7998 }
7999 if (jp->used == 0)
8000 break;
8001 if (jp->state != JOBDONE || !jp->waited)
8002 continue;
8003#if JOBS
8004 if (jobctl)
8005 continue;
8006#endif
8007 freejob(jp);
8008 break;
8009 }
8010 memset(jp, 0, sizeof(*jp));
8011#if JOBS
8012 if (jobctl)
8013 jp->jobctl = 1;
8014#endif
8015 jp->prev_job = curjob;
8016 curjob = jp;
8017 jp->used = 1;
8018 jp->ps = &jp->ps0;
8019 if (nprocs > 1) {
8020 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
8021 }
8022 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
8023 jobno(jp)));
8024 return jp;
8025}
8026
Denis Vlasenkoa6704932007-02-23 01:05:15 +00008027#if JOBS
8028/*
8029 * Return a string identifying a command (to be printed by the
8030 * jobs command).
8031 */
8032static char *cmdnextc;
8033
8034static void
8035cmdputs(const char *s)
8036{
8037 const char *p, *str;
8038 char c, cc[2] = " ";
8039 char *nextc;
8040 int subtype = 0;
8041 int quoted = 0;
8042 static const char vstype[VSTYPE + 1][4] = {
8043 "", "}", "-", "+", "?", "=",
8044 "%", "%%", "#", "##"
8045 };
8046
8047 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
8048 p = s;
8049 while ((c = *p++) != 0) {
8050 str = 0;
8051 switch (c) {
8052 case CTLESC:
8053 c = *p++;
8054 break;
8055 case CTLVAR:
8056 subtype = *p++;
8057 if ((subtype & VSTYPE) == VSLENGTH)
8058 str = "${#";
8059 else
8060 str = "${";
8061 if (!(subtype & VSQUOTE) == !(quoted & 1))
8062 goto dostr;
8063 quoted ^= 1;
8064 c = '"';
8065 break;
8066 case CTLENDVAR:
8067 str = "\"}" + !(quoted & 1);
8068 quoted >>= 1;
8069 subtype = 0;
8070 goto dostr;
8071 case CTLBACKQ:
8072 str = "$(...)";
8073 goto dostr;
8074 case CTLBACKQ+CTLQUOTE:
8075 str = "\"$(...)\"";
8076 goto dostr;
8077#if ENABLE_ASH_MATH_SUPPORT
8078 case CTLARI:
8079 str = "$((";
8080 goto dostr;
8081 case CTLENDARI:
8082 str = "))";
8083 goto dostr;
8084#endif
8085 case CTLQUOTEMARK:
8086 quoted ^= 1;
8087 c = '"';
8088 break;
8089 case '=':
8090 if (subtype == 0)
8091 break;
8092 if ((subtype & VSTYPE) != VSNORMAL)
8093 quoted <<= 1;
8094 str = vstype[subtype & VSTYPE];
8095 if (subtype & VSNUL)
8096 c = ':';
8097 else
8098 goto checkstr;
8099 break;
8100 case '\'':
8101 case '\\':
8102 case '"':
8103 case '$':
8104 /* These can only happen inside quotes */
8105 cc[0] = c;
8106 str = cc;
8107 c = '\\';
8108 break;
8109 default:
8110 break;
8111 }
8112 USTPUTC(c, nextc);
8113 checkstr:
8114 if (!str)
8115 continue;
8116 dostr:
8117 while ((c = *str++)) {
8118 USTPUTC(c, nextc);
8119 }
8120 }
8121 if (quoted & 1) {
8122 USTPUTC('"', nextc);
8123 }
8124 *nextc = 0;
8125 cmdnextc = nextc;
8126}
8127
8128/* cmdtxt() and cmdlist() call each other */
8129static void cmdtxt(union node *n);
8130
8131static void
8132cmdlist(union node *np, int sep)
8133{
8134 for (; np; np = np->narg.next) {
8135 if (!sep)
8136 cmdputs(spcstr);
8137 cmdtxt(np);
8138 if (sep && np->narg.next)
8139 cmdputs(spcstr);
8140 }
8141}
8142
8143static void
8144cmdtxt(union node *n)
8145{
8146 union node *np;
8147 struct nodelist *lp;
8148 const char *p;
8149 char s[2];
8150
8151 if (!n)
8152 return;
8153 switch (n->type) {
8154 default:
8155#if DEBUG
8156 abort();
8157#endif
8158 case NPIPE:
8159 lp = n->npipe.cmdlist;
8160 for (;;) {
8161 cmdtxt(lp->n);
8162 lp = lp->next;
8163 if (!lp)
8164 break;
8165 cmdputs(" | ");
8166 }
8167 break;
8168 case NSEMI:
8169 p = "; ";
8170 goto binop;
8171 case NAND:
8172 p = " && ";
8173 goto binop;
8174 case NOR:
8175 p = " || ";
8176 binop:
8177 cmdtxt(n->nbinary.ch1);
8178 cmdputs(p);
8179 n = n->nbinary.ch2;
8180 goto donode;
8181 case NREDIR:
8182 case NBACKGND:
8183 n = n->nredir.n;
8184 goto donode;
8185 case NNOT:
8186 cmdputs("!");
8187 n = n->nnot.com;
8188 donode:
8189 cmdtxt(n);
8190 break;
8191 case NIF:
8192 cmdputs("if ");
8193 cmdtxt(n->nif.test);
8194 cmdputs("; then ");
8195 n = n->nif.ifpart;
8196 if (n->nif.elsepart) {
8197 cmdtxt(n);
8198 cmdputs("; else ");
8199 n = n->nif.elsepart;
8200 }
8201 p = "; fi";
8202 goto dotail;
8203 case NSUBSHELL:
8204 cmdputs("(");
8205 n = n->nredir.n;
8206 p = ")";
8207 goto dotail;
8208 case NWHILE:
8209 p = "while ";
8210 goto until;
8211 case NUNTIL:
8212 p = "until ";
8213 until:
8214 cmdputs(p);
8215 cmdtxt(n->nbinary.ch1);
8216 n = n->nbinary.ch2;
8217 p = "; done";
8218 dodo:
8219 cmdputs("; do ");
8220 dotail:
8221 cmdtxt(n);
8222 goto dotail2;
8223 case NFOR:
8224 cmdputs("for ");
8225 cmdputs(n->nfor.var);
8226 cmdputs(" in ");
8227 cmdlist(n->nfor.args, 1);
8228 n = n->nfor.body;
8229 p = "; done";
8230 goto dodo;
8231 case NDEFUN:
8232 cmdputs(n->narg.text);
8233 p = "() { ... }";
8234 goto dotail2;
8235 case NCMD:
8236 cmdlist(n->ncmd.args, 1);
8237 cmdlist(n->ncmd.redirect, 0);
8238 break;
8239 case NARG:
8240 p = n->narg.text;
8241 dotail2:
8242 cmdputs(p);
8243 break;
8244 case NHERE:
8245 case NXHERE:
8246 p = "<<...";
8247 goto dotail2;
8248 case NCASE:
8249 cmdputs("case ");
8250 cmdputs(n->ncase.expr->narg.text);
8251 cmdputs(" in ");
8252 for (np = n->ncase.cases; np; np = np->nclist.next) {
8253 cmdtxt(np->nclist.pattern);
8254 cmdputs(") ");
8255 cmdtxt(np->nclist.body);
8256 cmdputs(";; ");
8257 }
8258 p = "esac";
8259 goto dotail2;
8260 case NTO:
8261 p = ">";
8262 goto redir;
8263 case NCLOBBER:
8264 p = ">|";
8265 goto redir;
8266 case NAPPEND:
8267 p = ">>";
8268 goto redir;
8269 case NTOFD:
8270 p = ">&";
8271 goto redir;
8272 case NFROM:
8273 p = "<";
8274 goto redir;
8275 case NFROMFD:
8276 p = "<&";
8277 goto redir;
8278 case NFROMTO:
8279 p = "<>";
8280 redir:
8281 s[0] = n->nfile.fd + '0';
8282 s[1] = '\0';
8283 cmdputs(s);
8284 cmdputs(p);
8285 if (n->type == NTOFD || n->type == NFROMFD) {
8286 s[0] = n->ndup.dupfd + '0';
8287 p = s;
8288 goto dotail2;
8289 }
8290 n = n->nfile.fname;
8291 goto donode;
8292 }
8293}
8294
8295static char *
8296commandtext(union node *n)
8297{
8298 char *name;
8299
8300 STARTSTACKSTR(cmdnextc);
8301 cmdtxt(n);
8302 name = stackblock();
8303 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
8304 name, cmdnextc, cmdnextc));
8305 return ckstrdup(name);
8306}
8307#endif /* JOBS */
8308
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00008309/*
8310 * Fork off a subshell. If we are doing job control, give the subshell its
8311 * own process group. Jp is a job structure that the job is to be added to.
8312 * N is the command that will be evaluated by the child. Both jp and n may
8313 * be NULL. The mode parameter can be one of the following:
8314 * FORK_FG - Fork off a foreground process.
8315 * FORK_BG - Fork off a background process.
8316 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
8317 * process group even if job control is on.
8318 *
8319 * When job control is turned off, background processes have their standard
8320 * input redirected to /dev/null (except for the second and later processes
8321 * in a pipeline).
8322 *
8323 * Called with interrupts off.
8324 */
8325static void
8326forkchild(struct job *jp, union node *n, int mode)
8327{
8328 int oldlvl;
8329
8330 TRACE(("Child shell %d\n", getpid()));
8331 oldlvl = shlvl;
8332 shlvl++;
8333
8334 closescript();
8335 clear_traps();
8336#if JOBS
8337 /* do job control only in root shell */
8338 jobctl = 0;
8339 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
8340 pid_t pgrp;
8341
8342 if (jp->nprocs == 0)
8343 pgrp = getpid();
8344 else
8345 pgrp = jp->ps[0].pid;
8346 /* This can fail because we are doing it in the parent also */
8347 (void)setpgid(0, pgrp);
8348 if (mode == FORK_FG)
8349 xtcsetpgrp(ttyfd, pgrp);
8350 setsignal(SIGTSTP);
8351 setsignal(SIGTTOU);
8352 } else
8353#endif
8354 if (mode == FORK_BG) {
8355 ignoresig(SIGINT);
8356 ignoresig(SIGQUIT);
8357 if (jp->nprocs == 0) {
8358 close(0);
8359 if (open(bb_dev_null, O_RDONLY) != 0)
8360 ash_msg_and_raise_error("Can't open %s", bb_dev_null);
8361 }
8362 }
8363 if (!oldlvl && iflag) {
8364 setsignal(SIGINT);
8365 setsignal(SIGQUIT);
8366 setsignal(SIGTERM);
8367 }
8368 for (jp = curjob; jp; jp = jp->prev_job)
8369 freejob(jp);
8370 jobless = 0;
8371}
8372
8373static void
8374forkparent(struct job *jp, union node *n, int mode, pid_t pid)
8375{
8376 TRACE(("In parent shell: child = %d\n", pid));
8377 if (!jp) {
8378 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
8379 jobless++;
8380 return;
8381 }
8382#if JOBS
8383 if (mode != FORK_NOJOB && jp->jobctl) {
8384 int pgrp;
8385
8386 if (jp->nprocs == 0)
8387 pgrp = pid;
8388 else
8389 pgrp = jp->ps[0].pid;
8390 /* This can fail because we are doing it in the child also */
8391 setpgid(pid, pgrp);
8392 }
8393#endif
8394 if (mode == FORK_BG) {
8395 backgndpid = pid; /* set $! */
8396 set_curjob(jp, CUR_RUNNING);
8397 }
8398 if (jp) {
8399 struct procstat *ps = &jp->ps[jp->nprocs++];
8400 ps->pid = pid;
8401 ps->status = -1;
8402 ps->cmd = nullstr;
8403#if JOBS
8404 if (jobctl && n)
8405 ps->cmd = commandtext(n);
8406#endif
8407 }
8408}
8409
8410static int
8411forkshell(struct job *jp, union node *n, int mode)
8412{
8413 int pid;
8414
8415 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
8416 pid = fork();
8417 if (pid < 0) {
8418 TRACE(("Fork failed, errno=%d", errno));
8419 if (jp)
8420 freejob(jp);
8421 ash_msg_and_raise_error("Cannot fork");
8422 }
8423 if (pid == 0)
8424 forkchild(jp, n, mode);
8425 else
8426 forkparent(jp, n, mode, pid);
8427 return pid;
8428}
8429
8430/*
8431 * Wait for job to finish.
8432 *
8433 * Under job control we have the problem that while a child process is
8434 * running interrupts generated by the user are sent to the child but not
8435 * to the shell. This means that an infinite loop started by an inter-
8436 * active user may be hard to kill. With job control turned off, an
8437 * interactive user may place an interactive program inside a loop. If
8438 * the interactive program catches interrupts, the user doesn't want
8439 * these interrupts to also abort the loop. The approach we take here
8440 * is to have the shell ignore interrupt signals while waiting for a
8441 * foreground process to terminate, and then send itself an interrupt
8442 * signal if the child process was terminated by an interrupt signal.
8443 * Unfortunately, some programs want to do a bit of cleanup and then
8444 * exit on interrupt; unless these processes terminate themselves by
8445 * sending a signal to themselves (instead of calling exit) they will
8446 * confuse this approach.
8447 *
8448 * Called with interrupts off.
8449 */
8450static int
8451waitforjob(struct job *jp)
8452{
8453 int st;
8454
8455 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
8456 while (jp->state == JOBRUNNING) {
8457 dowait(DOWAIT_BLOCK, jp);
8458 }
8459 st = getstatus(jp);
8460#if JOBS
8461 if (jp->jobctl) {
8462 xtcsetpgrp(ttyfd, rootpid);
8463 /*
8464 * This is truly gross.
8465 * If we're doing job control, then we did a TIOCSPGRP which
8466 * caused us (the shell) to no longer be in the controlling
8467 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
8468 * intuit from the subprocess exit status whether a SIGINT
8469 * occurred, and if so interrupt ourselves. Yuck. - mycroft
8470 */
8471 if (jp->sigint)
8472 raise(SIGINT);
8473 }
8474 if (jp->state == JOBDONE)
8475#endif
8476 freejob(jp);
8477 return st;
8478}
Eric Andersencb57d552001-06-28 07:25:16 +00008479
Eric Andersencb57d552001-06-28 07:25:16 +00008480/*
8481 * return 1 if there are stopped jobs, otherwise 0
8482 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +00008483static int
Eric Andersenc470f442003-07-28 09:56:35 +00008484stoppedjobs(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008485{
Eric Andersencb57d552001-06-28 07:25:16 +00008486 struct job *jp;
Eric Andersenc470f442003-07-28 09:56:35 +00008487 int retval;
Eric Andersencb57d552001-06-28 07:25:16 +00008488
Eric Andersenc470f442003-07-28 09:56:35 +00008489 retval = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008490 if (job_warning)
Eric Andersenc470f442003-07-28 09:56:35 +00008491 goto out;
8492 jp = curjob;
8493 if (jp && jp->state == JOBSTOPPED) {
8494 out2str("You have stopped jobs.\n");
8495 job_warning = 2;
8496 retval++;
Eric Andersencb57d552001-06-28 07:25:16 +00008497 }
Denis Vlasenkoa6704932007-02-23 01:05:15 +00008498 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008499 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00008500}
8501
Eric Andersenc470f442003-07-28 09:56:35 +00008502
Denis Vlasenko131ae172007-02-18 13:00:19 +00008503#if ENABLE_ASH_MAIL
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008504/* mail.c */
Eric Andersenec074692001-10-31 11:05:49 +00008505
Eric Andersencb57d552001-06-28 07:25:16 +00008506/*
Eric Andersenc470f442003-07-28 09:56:35 +00008507 * Routines to check for mail. (Perhaps make part of main.c?)
Eric Andersencb57d552001-06-28 07:25:16 +00008508 */
8509
Eric Andersencb57d552001-06-28 07:25:16 +00008510#define MAXMBOXES 10
8511
Eric Andersenc470f442003-07-28 09:56:35 +00008512/* times of mailboxes */
8513static time_t mailtime[MAXMBOXES];
8514/* Set if MAIL or MAILPATH is changed. */
8515static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008516
8517
Eric Andersencb57d552001-06-28 07:25:16 +00008518/*
Eric Andersenc470f442003-07-28 09:56:35 +00008519 * Print appropriate message(s) if mail has arrived.
8520 * If mail_var_path_changed is set,
8521 * then the value of MAIL has mail_var_path_changed,
8522 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008523 */
Eric Andersenc470f442003-07-28 09:56:35 +00008524static void
8525chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008526{
Eric Andersencb57d552001-06-28 07:25:16 +00008527 const char *mpath;
8528 char *p;
8529 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008530 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008531 struct stackmark smark;
8532 struct stat statb;
8533
Eric Andersencb57d552001-06-28 07:25:16 +00008534 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008535 mpath = mpathset() ? mpathval() : mailval();
8536 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008537 p = padvance(&mpath, nullstr);
8538 if (p == NULL)
8539 break;
8540 if (*p == '\0')
8541 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008542 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008543#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008544 if (q[-1] != '/')
8545 abort();
8546#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008547 q[-1] = '\0'; /* delete trailing '/' */
8548 if (stat(p, &statb) < 0) {
8549 *mtp = 0;
8550 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008551 }
Eric Andersenc470f442003-07-28 09:56:35 +00008552 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8553 fprintf(
8554 stderr, snlfmt,
8555 pathopt ? pathopt : "you have mail"
8556 );
8557 }
8558 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008559 }
Eric Andersenc470f442003-07-28 09:56:35 +00008560 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008561 popstackmark(&smark);
8562}
Eric Andersencb57d552001-06-28 07:25:16 +00008563
Eric Andersenec074692001-10-31 11:05:49 +00008564
Eric Andersenc470f442003-07-28 09:56:35 +00008565static void
8566changemail(const char *val)
8567{
8568 mail_var_path_changed++;
8569}
8570
Denis Vlasenko131ae172007-02-18 13:00:19 +00008571#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008572
Eric Andersencb57d552001-06-28 07:25:16 +00008573/*
Eric Andersenc470f442003-07-28 09:56:35 +00008574 * Take commands from a file. To be compatible we should do a path
Eric Andersencb57d552001-06-28 07:25:16 +00008575 * search for the file, which is necessary to find sub-commands.
8576 */
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008577static char *
8578find_dot_file(char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00008579{
8580 char *fullname;
8581 const char *path = pathval();
8582 struct stat statb;
8583
8584 /* don't try this for absolute or relative paths */
Eric Andersenc470f442003-07-28 09:56:35 +00008585 if (strchr(name, '/'))
8586 return name;
Eric Andersencb57d552001-06-28 07:25:16 +00008587
Eric Andersenc470f442003-07-28 09:56:35 +00008588 while ((fullname = padvance(&path, name)) != NULL) {
Eric Andersencb57d552001-06-28 07:25:16 +00008589 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
8590 /*
8591 * Don't bother freeing here, since it will
8592 * be freed by the caller.
8593 */
8594 return fullname;
8595 }
8596 stunalloc(fullname);
8597 }
8598
8599 /* not found in the PATH */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008600 ash_msg_and_raise_error("%s: not found", name);
Eric Andersencb57d552001-06-28 07:25:16 +00008601 /* NOTREACHED */
8602}
8603
Eric Andersenc470f442003-07-28 09:56:35 +00008604static void
8605calcsize(union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008606{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008607 if (n == NULL)
8608 return;
8609 funcblocksize += nodesize[n->type];
8610 switch (n->type) {
8611 case NCMD:
8612 calcsize(n->ncmd.redirect);
8613 calcsize(n->ncmd.args);
8614 calcsize(n->ncmd.assign);
8615 break;
8616 case NPIPE:
8617 sizenodelist(n->npipe.cmdlist);
8618 break;
8619 case NREDIR:
8620 case NBACKGND:
8621 case NSUBSHELL:
8622 calcsize(n->nredir.redirect);
8623 calcsize(n->nredir.n);
8624 break;
8625 case NAND:
8626 case NOR:
8627 case NSEMI:
8628 case NWHILE:
8629 case NUNTIL:
8630 calcsize(n->nbinary.ch2);
8631 calcsize(n->nbinary.ch1);
8632 break;
8633 case NIF:
8634 calcsize(n->nif.elsepart);
8635 calcsize(n->nif.ifpart);
8636 calcsize(n->nif.test);
8637 break;
8638 case NFOR:
8639 funcstringsize += strlen(n->nfor.var) + 1;
8640 calcsize(n->nfor.body);
8641 calcsize(n->nfor.args);
8642 break;
8643 case NCASE:
8644 calcsize(n->ncase.cases);
8645 calcsize(n->ncase.expr);
8646 break;
8647 case NCLIST:
8648 calcsize(n->nclist.body);
8649 calcsize(n->nclist.pattern);
8650 calcsize(n->nclist.next);
8651 break;
8652 case NDEFUN:
8653 case NARG:
8654 sizenodelist(n->narg.backquote);
8655 funcstringsize += strlen(n->narg.text) + 1;
8656 calcsize(n->narg.next);
8657 break;
8658 case NTO:
8659 case NCLOBBER:
8660 case NFROM:
8661 case NFROMTO:
8662 case NAPPEND:
8663 calcsize(n->nfile.fname);
8664 calcsize(n->nfile.next);
8665 break;
8666 case NTOFD:
8667 case NFROMFD:
8668 calcsize(n->ndup.vname);
8669 calcsize(n->ndup.next);
8670 break;
8671 case NHERE:
8672 case NXHERE:
8673 calcsize(n->nhere.doc);
8674 calcsize(n->nhere.next);
8675 break;
8676 case NNOT:
8677 calcsize(n->nnot.com);
8678 break;
8679 };
Eric Andersencb57d552001-06-28 07:25:16 +00008680}
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008681
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008682
Eric Andersenc470f442003-07-28 09:56:35 +00008683static void
8684sizenodelist(struct nodelist *lp)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008685{
8686 while (lp) {
Eric Andersenc470f442003-07-28 09:56:35 +00008687 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008688 calcsize(lp->n);
8689 lp = lp->next;
8690 }
8691}
Eric Andersencb57d552001-06-28 07:25:16 +00008692
8693
Eric Andersenc470f442003-07-28 09:56:35 +00008694static union node *
8695copynode(union node *n)
8696{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008697 union node *new;
Eric Andersenc470f442003-07-28 09:56:35 +00008698
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008699 if (n == NULL)
8700 return NULL;
8701 new = funcblock;
8702 funcblock = (char *) funcblock + nodesize[n->type];
8703
8704 switch (n->type) {
8705 case NCMD:
8706 new->ncmd.redirect = copynode(n->ncmd.redirect);
8707 new->ncmd.args = copynode(n->ncmd.args);
8708 new->ncmd.assign = copynode(n->ncmd.assign);
8709 break;
8710 case NPIPE:
8711 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8712 new->npipe.backgnd = n->npipe.backgnd;
8713 break;
8714 case NREDIR:
8715 case NBACKGND:
8716 case NSUBSHELL:
8717 new->nredir.redirect = copynode(n->nredir.redirect);
8718 new->nredir.n = copynode(n->nredir.n);
8719 break;
8720 case NAND:
8721 case NOR:
8722 case NSEMI:
8723 case NWHILE:
8724 case NUNTIL:
8725 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8726 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8727 break;
8728 case NIF:
8729 new->nif.elsepart = copynode(n->nif.elsepart);
8730 new->nif.ifpart = copynode(n->nif.ifpart);
8731 new->nif.test = copynode(n->nif.test);
8732 break;
8733 case NFOR:
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008734 new->nfor.var = nodeckstrdup(n->nfor.var);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008735 new->nfor.body = copynode(n->nfor.body);
8736 new->nfor.args = copynode(n->nfor.args);
8737 break;
8738 case NCASE:
8739 new->ncase.cases = copynode(n->ncase.cases);
8740 new->ncase.expr = copynode(n->ncase.expr);
8741 break;
8742 case NCLIST:
8743 new->nclist.body = copynode(n->nclist.body);
8744 new->nclist.pattern = copynode(n->nclist.pattern);
8745 new->nclist.next = copynode(n->nclist.next);
8746 break;
8747 case NDEFUN:
8748 case NARG:
8749 new->narg.backquote = copynodelist(n->narg.backquote);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008750 new->narg.text = nodeckstrdup(n->narg.text);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008751 new->narg.next = copynode(n->narg.next);
8752 break;
8753 case NTO:
8754 case NCLOBBER:
8755 case NFROM:
8756 case NFROMTO:
8757 case NAPPEND:
8758 new->nfile.fname = copynode(n->nfile.fname);
8759 new->nfile.fd = n->nfile.fd;
8760 new->nfile.next = copynode(n->nfile.next);
8761 break;
8762 case NTOFD:
8763 case NFROMFD:
8764 new->ndup.vname = copynode(n->ndup.vname);
8765 new->ndup.dupfd = n->ndup.dupfd;
8766 new->ndup.fd = n->ndup.fd;
8767 new->ndup.next = copynode(n->ndup.next);
8768 break;
8769 case NHERE:
8770 case NXHERE:
8771 new->nhere.doc = copynode(n->nhere.doc);
8772 new->nhere.fd = n->nhere.fd;
8773 new->nhere.next = copynode(n->nhere.next);
8774 break;
8775 case NNOT:
8776 new->nnot.com = copynode(n->nnot.com);
8777 break;
8778 };
8779 new->type = n->type;
Eric Andersenc470f442003-07-28 09:56:35 +00008780 return new;
8781}
8782
8783
8784static struct nodelist *
8785copynodelist(struct nodelist *lp)
Eric Andersencb57d552001-06-28 07:25:16 +00008786{
8787 struct nodelist *start;
8788 struct nodelist **lpp;
8789
8790 lpp = &start;
8791 while (lp) {
8792 *lpp = funcblock;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008793 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00008794 (*lpp)->n = copynode(lp->n);
8795 lp = lp->next;
8796 lpp = &(*lpp)->next;
8797 }
8798 *lpp = NULL;
8799 return start;
8800}
8801
8802
Eric Andersenc470f442003-07-28 09:56:35 +00008803static char *
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008804nodeckstrdup(char *s)
Eric Andersenc470f442003-07-28 09:56:35 +00008805{
Denis Vlasenko7cfecc42006-12-18 22:32:45 +00008806 char *rtn = funcstring;
Eric Andersenc470f442003-07-28 09:56:35 +00008807
Denis Vlasenko7cfecc42006-12-18 22:32:45 +00008808 strcpy(funcstring, s);
8809 funcstring += strlen(s) + 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008810 return rtn;
Eric Andersencb57d552001-06-28 07:25:16 +00008811}
8812
Eric Andersenc470f442003-07-28 09:56:35 +00008813
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008814#if ENABLE_FEATURE_EDITING_VI
8815#define setvimode(on) do { \
8816 if (on) line_input_state->flags |= VI_MODE; \
8817 else line_input_state->flags &= ~VI_MODE; \
8818} while (0)
8819#else
8820#define setvimode(on) viflag = 0 /* forcibly keep the option off */
8821#endif
8822
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008823static void
Eric Andersenc470f442003-07-28 09:56:35 +00008824optschanged(void)
8825{
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008826#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008827 opentrace();
8828#endif
8829 setinteractive(iflag);
8830 setjobctl(mflag);
Paul Fox3f11b1b2005-08-04 19:04:46 +00008831 setvimode(viflag);
Eric Andersenc470f442003-07-28 09:56:35 +00008832}
Eric Andersencb57d552001-06-28 07:25:16 +00008833
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008834static void
8835minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008836{
8837 int i;
8838
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008839 if (name) {
8840 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008841 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008842 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00008843 return;
8844 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008845 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008846 ash_msg_and_raise_error("Illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00008847 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008848 out1str("Current option settings\n");
8849 for (i = 0; i < NOPTS; i++)
8850 out1fmt("%-16s%s\n", optnames(i),
8851 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00008852}
8853
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008854
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008855static void
8856setoption(int flag, int val)
8857{
8858 int i;
8859
8860 for (i = 0; i < NOPTS; i++) {
8861 if (optletters(i) == flag) {
8862 optlist[i] = val;
8863 return;
8864 }
8865 }
8866 ash_msg_and_raise_error("Illegal option -%c", flag);
8867 /* NOTREACHED */
8868}
8869
8870
Eric Andersenc470f442003-07-28 09:56:35 +00008871/*
8872 * Process shell options. The global variable argptr contains a pointer
8873 * to the argument list; we advance it past the options.
8874 */
Eric Andersenc470f442003-07-28 09:56:35 +00008875static void
8876options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00008877{
8878 char *p;
8879 int val;
8880 int c;
8881
8882 if (cmdline)
8883 minusc = NULL;
8884 while ((p = *argptr) != NULL) {
8885 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008886 c = *p++;
8887 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00008888 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00008889 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00008890 if (!cmdline) {
8891 /* "-" means turn off -x and -v */
8892 if (p[0] == '\0')
8893 xflag = vflag = 0;
8894 /* "--" means reset params */
8895 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00008896 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00008897 }
Eric Andersenc470f442003-07-28 09:56:35 +00008898 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00008899 }
8900 } else if (c == '+') {
8901 val = 0;
8902 } else {
8903 argptr--;
8904 break;
8905 }
8906 while ((c = *p++) != '\0') {
8907 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00008908 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00008909 } else if (c == 'o') {
8910 minus_o(*argptr, val);
8911 if (*argptr)
8912 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00008913 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008914 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00008915 isloginsh = 1;
8916 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008917 } else {
8918 setoption(c, val);
8919 }
8920 }
8921 }
8922}
8923
Eric Andersencb57d552001-06-28 07:25:16 +00008924
Eric Andersencb57d552001-06-28 07:25:16 +00008925/*
8926 * Set the shell parameters.
8927 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008928static void
Eric Andersenc470f442003-07-28 09:56:35 +00008929setparam(char **argv)
Eric Andersen2870d962001-07-02 17:27:21 +00008930{
Eric Andersencb57d552001-06-28 07:25:16 +00008931 char **newparam;
8932 char **ap;
8933 int nparam;
8934
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008935 for (nparam = 0; argv[nparam]; nparam++);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008936 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
Eric Andersencb57d552001-06-28 07:25:16 +00008937 while (*argv) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008938 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008939 }
8940 *ap = NULL;
8941 freeparam(&shellparam);
8942 shellparam.malloc = 1;
8943 shellparam.nparam = nparam;
8944 shellparam.p = newparam;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008945#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00008946 shellparam.optind = 1;
8947 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00008948#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008949}
8950
8951
8952/*
8953 * Free the list of positional parameters.
8954 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008955static void
Eric Andersenc470f442003-07-28 09:56:35 +00008956freeparam(volatile struct shparam *param)
Eric Andersen2870d962001-07-02 17:27:21 +00008957{
Eric Andersencb57d552001-06-28 07:25:16 +00008958 char **ap;
8959
8960 if (param->malloc) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008961 for (ap = param->p; *ap; ap++)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008962 free(*ap);
8963 free(param->p);
Eric Andersencb57d552001-06-28 07:25:16 +00008964 }
8965}
8966
8967
Eric Andersencb57d552001-06-28 07:25:16 +00008968/*
8969 * The shift builtin command.
8970 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008971static int
Eric Andersenc470f442003-07-28 09:56:35 +00008972shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008973{
8974 int n;
8975 char **ap1, **ap2;
8976
8977 n = 1;
8978 if (argc > 1)
8979 n = number(argv[1]);
8980 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008981 ash_msg_and_raise_error("can't shift that many");
8982 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008983 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008984 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008985 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008986 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00008987 }
8988 ap2 = shellparam.p;
8989 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00008990#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00008991 shellparam.optind = 1;
8992 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00008993#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00008994 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008995 return 0;
8996}
8997
8998
Eric Andersencb57d552001-06-28 07:25:16 +00008999/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009000 * POSIX requires that 'set' (but not export or readonly) output the
9001 * variables in lexicographic order - by the locale's collating order (sigh).
9002 * Maybe we could keep them in an ordered balanced binary tree
9003 * instead of hashed lists.
9004 * For now just roll 'em through qsort for printing...
9005 */
9006static int
9007showvars(const char *sep_prefix, int on, int off)
9008{
9009 const char *sep;
9010 char **ep, **epend;
9011
9012 ep = listvars(on, off, &epend);
9013 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9014
9015 sep = *sep_prefix ? spcstr : sep_prefix;
9016
9017 for (; ep < epend; ep++) {
9018 const char *p;
9019 const char *q;
9020
9021 p = strchrnul(*ep, '=');
9022 q = nullstr;
9023 if (*p)
9024 q = single_quote(++p);
9025 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9026 }
9027 return 0;
9028}
9029
9030/*
Eric Andersencb57d552001-06-28 07:25:16 +00009031 * The set command builtin.
9032 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009033static int
Eric Andersenc470f442003-07-28 09:56:35 +00009034setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009035{
9036 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009037 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009038 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009039 options(0);
9040 optschanged();
9041 if (*argptr != NULL) {
9042 setparam(argptr);
9043 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009044 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009045 return 0;
9046}
9047
9048
Denis Vlasenko131ae172007-02-18 13:00:19 +00009049#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009050static void
9051change_lc_all(const char *value)
Eric Andersen2870d962001-07-02 17:27:21 +00009052{
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009053 if (value && *value != '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00009054 setlocale(LC_ALL, value);
9055}
9056
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009057static void
9058change_lc_ctype(const char *value)
Eric Andersen2870d962001-07-02 17:27:21 +00009059{
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009060 if (value && *value != '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00009061 setlocale(LC_CTYPE, value);
9062}
Eric Andersen2870d962001-07-02 17:27:21 +00009063#endif
9064
Denis Vlasenko131ae172007-02-18 13:00:19 +00009065#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009066/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009067static void
9068change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009069{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009070 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009071 /* "get", generate */
9072 char buf[16];
9073
9074 rseed = rseed * 1103515245 + 12345;
9075 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9076 /* set without recursion */
9077 setvar(vrandom.text, buf, VNOFUNC);
9078 vrandom.flags &= ~VNOFUNC;
9079 } else {
9080 /* set/reset */
9081 rseed = strtoul(value, (char **)NULL, 10);
9082 }
Eric Andersenef02f822004-03-11 13:34:24 +00009083}
Eric Andersen16767e22004-03-16 05:14:10 +00009084#endif
9085
Eric Andersenef02f822004-03-11 13:34:24 +00009086
Denis Vlasenko131ae172007-02-18 13:00:19 +00009087#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009088static int
Eric Andersenc470f442003-07-28 09:56:35 +00009089getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009090{
9091 char *p, *q;
9092 char c = '?';
9093 int done = 0;
9094 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009095 char s[12];
9096 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009097
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009098 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009099 return 1;
9100 optnext = optfirst + *param_optind - 1;
9101
9102 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009103 p = NULL;
9104 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009105 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009106 if (p == NULL || *p == '\0') {
9107 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009108 p = *optnext;
9109 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009110 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009111 p = NULL;
9112 done = 1;
9113 goto out;
9114 }
9115 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009116 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009117 goto atend;
9118 }
9119
9120 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009121 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009122 if (*q == '\0') {
9123 if (optstr[0] == ':') {
9124 s[0] = c;
9125 s[1] = '\0';
9126 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009127 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009128 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009129 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009130 }
9131 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009132 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009133 }
9134 if (*++q == ':')
9135 q++;
9136 }
9137
9138 if (*++q == ':') {
9139 if (*p == '\0' && (p = *optnext) == NULL) {
9140 if (optstr[0] == ':') {
9141 s[0] = c;
9142 s[1] = '\0';
9143 err |= setvarsafe("OPTARG", s, 0);
9144 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009145 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009146 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009147 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009148 c = '?';
9149 }
Eric Andersenc470f442003-07-28 09:56:35 +00009150 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009151 }
9152
9153 if (p == *optnext)
9154 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009155 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009156 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009157 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009158 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009159 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009160 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009161 *param_optind = optnext - optfirst + 1;
9162 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009163 err |= setvarsafe("OPTIND", s, VNOFUNC);
9164 s[0] = c;
9165 s[1] = '\0';
9166 err |= setvarsafe(optvar, s, 0);
9167 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009168 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009169 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009170 flush_stdout_stderr();
9171 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009172 }
9173 return done;
9174}
Eric Andersenc470f442003-07-28 09:56:35 +00009175
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009176
Eric Andersenc470f442003-07-28 09:56:35 +00009177/*
9178 * The getopts builtin. Shellparam.optnext points to the next argument
9179 * to be processed. Shellparam.optptr points to the next character to
9180 * be processed in the current argument. If shellparam.optnext is NULL,
9181 * then it's the first time getopts has been called.
9182 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009183static int
Eric Andersenc470f442003-07-28 09:56:35 +00009184getoptscmd(int argc, char **argv)
9185{
9186 char **optbase;
9187
9188 if (argc < 3)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009189 ash_msg_and_raise_error("Usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009190 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009191 optbase = shellparam.p;
9192 if (shellparam.optind > shellparam.nparam + 1) {
9193 shellparam.optind = 1;
9194 shellparam.optoff = -1;
9195 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009196 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009197 optbase = &argv[3];
9198 if (shellparam.optind > argc - 2) {
9199 shellparam.optind = 1;
9200 shellparam.optoff = -1;
9201 }
9202 }
9203
9204 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009205 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009206}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009207#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009208
Eric Andersencb57d552001-06-28 07:25:16 +00009209
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009210/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009211
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009212static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9213static void
9214raise_error_syntax(const char *msg)
9215{
9216 ash_msg_and_raise_error("Syntax error: %s", msg);
9217 /* NOTREACHED */
9218}
9219
9220/*
9221 * Called when an unexpected token is read during the parse. The argument
9222 * is the token that is expected, or -1 if more than one type of token can
9223 * occur at this point.
9224 */
9225static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9226static void
9227raise_error_unexpected_syntax(int token)
9228{
9229 char msg[64];
9230 int l;
9231
9232 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9233 if (token >= 0)
9234 sprintf(msg + l, " (expecting %s)", tokname(token));
9235 raise_error_syntax(msg);
9236 /* NOTREACHED */
9237}
Eric Andersencb57d552001-06-28 07:25:16 +00009238
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009239#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009240
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009241struct heredoc {
9242 struct heredoc *next; /* next here document in list */
9243 union node *here; /* redirection node */
9244 char *eofmark; /* string indicating end of input */
9245 int striptabs; /* if set, strip leading tabs */
9246};
Eric Andersencb57d552001-06-28 07:25:16 +00009247
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009248static struct heredoc *heredoclist; /* list of here documents to read */
9249
9250/* parsing is heavily cross-recursive, need these forward decls */
9251static union node *andor(void);
9252static union node *pipeline(void);
9253static union node *parse_command(void);
9254static void parseheredoc(void);
9255static char peektoken(void);
9256static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009257
Eric Andersenc470f442003-07-28 09:56:35 +00009258static union node *
9259list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009260{
9261 union node *n1, *n2, *n3;
9262 int tok;
9263
Eric Andersenc470f442003-07-28 09:56:35 +00009264 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9265 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009266 return NULL;
9267 n1 = NULL;
9268 for (;;) {
9269 n2 = andor();
9270 tok = readtoken();
9271 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009272 if (n2->type == NPIPE) {
9273 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009274 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009275 if (n2->type != NREDIR) {
9276 n3 = stalloc(sizeof(struct nredir));
9277 n3->nredir.n = n2;
9278 n3->nredir.redirect = NULL;
9279 n2 = n3;
9280 }
9281 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009282 }
9283 }
9284 if (n1 == NULL) {
9285 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009286 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009287 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009288 n3->type = NSEMI;
9289 n3->nbinary.ch1 = n1;
9290 n3->nbinary.ch2 = n2;
9291 n1 = n3;
9292 }
9293 switch (tok) {
9294 case TBACKGND:
9295 case TSEMI:
9296 tok = readtoken();
9297 /* fall through */
9298 case TNL:
9299 if (tok == TNL) {
9300 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009301 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009302 return n1;
9303 } else {
9304 tokpushback++;
9305 }
Eric Andersenc470f442003-07-28 09:56:35 +00009306 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009307 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009308 return n1;
9309 break;
9310 case TEOF:
9311 if (heredoclist)
9312 parseheredoc();
9313 else
Eric Andersenc470f442003-07-28 09:56:35 +00009314 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009315 return n1;
9316 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009317 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009318 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009319 tokpushback++;
9320 return n1;
9321 }
9322 }
9323}
9324
Eric Andersenc470f442003-07-28 09:56:35 +00009325static union node *
9326andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009327{
Eric Andersencb57d552001-06-28 07:25:16 +00009328 union node *n1, *n2, *n3;
9329 int t;
9330
Eric Andersencb57d552001-06-28 07:25:16 +00009331 n1 = pipeline();
9332 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009333 t = readtoken();
9334 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009335 t = NAND;
9336 } else if (t == TOR) {
9337 t = NOR;
9338 } else {
9339 tokpushback++;
9340 return n1;
9341 }
Eric Andersenc470f442003-07-28 09:56:35 +00009342 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009343 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009344 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009345 n3->type = t;
9346 n3->nbinary.ch1 = n1;
9347 n3->nbinary.ch2 = n2;
9348 n1 = n3;
9349 }
9350}
9351
Eric Andersenc470f442003-07-28 09:56:35 +00009352static union node *
9353pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009354{
Eric Andersencb57d552001-06-28 07:25:16 +00009355 union node *n1, *n2, *pipenode;
9356 struct nodelist *lp, *prev;
9357 int negate;
9358
9359 negate = 0;
9360 TRACE(("pipeline: entered\n"));
9361 if (readtoken() == TNOT) {
9362 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009363 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009364 } else
9365 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009366 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009367 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009368 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009369 pipenode->type = NPIPE;
9370 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009371 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009372 pipenode->npipe.cmdlist = lp;
9373 lp->n = n1;
9374 do {
9375 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009376 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009377 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009378 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009379 prev->next = lp;
9380 } while (readtoken() == TPIPE);
9381 lp->next = NULL;
9382 n1 = pipenode;
9383 }
9384 tokpushback++;
9385 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009386 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009387 n2->type = NNOT;
9388 n2->nnot.com = n1;
9389 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009390 }
9391 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009392}
9393
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009394static union node *
9395makename(void)
9396{
9397 union node *n;
9398
9399 n = stalloc(sizeof(struct narg));
9400 n->type = NARG;
9401 n->narg.next = NULL;
9402 n->narg.text = wordtext;
9403 n->narg.backquote = backquotelist;
9404 return n;
9405}
9406
9407static void
9408fixredir(union node *n, const char *text, int err)
9409{
9410 TRACE(("Fix redir %s %d\n", text, err));
9411 if (!err)
9412 n->ndup.vname = NULL;
9413
9414 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009415 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009416 else if (LONE_DASH(text))
9417 n->ndup.dupfd = -1;
9418 else {
9419 if (err)
9420 raise_error_syntax("Bad fd number");
9421 n->ndup.vname = makename();
9422 }
9423}
9424
9425/*
9426 * Returns true if the text contains nothing to expand (no dollar signs
9427 * or backquotes).
9428 */
9429static int
9430noexpand(char *text)
9431{
9432 char *p;
9433 char c;
9434
9435 p = text;
9436 while ((c = *p++) != '\0') {
9437 if (c == CTLQUOTEMARK)
9438 continue;
9439 if (c == CTLESC)
9440 p++;
9441 else if (SIT(c, BASESYNTAX) == CCTL)
9442 return 0;
9443 }
9444 return 1;
9445}
9446
9447static void
9448parsefname(void)
9449{
9450 union node *n = redirnode;
9451
9452 if (readtoken() != TWORD)
9453 raise_error_unexpected_syntax(-1);
9454 if (n->type == NHERE) {
9455 struct heredoc *here = heredoc;
9456 struct heredoc *p;
9457 int i;
9458
9459 if (quoteflag == 0)
9460 n->type = NXHERE;
9461 TRACE(("Here document %d\n", n->type));
9462 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9463 raise_error_syntax("Illegal eof marker for << redirection");
9464 rmescapes(wordtext);
9465 here->eofmark = wordtext;
9466 here->next = NULL;
9467 if (heredoclist == NULL)
9468 heredoclist = here;
9469 else {
9470 for (p = heredoclist; p->next; p = p->next);
9471 p->next = here;
9472 }
9473 } else if (n->type == NTOFD || n->type == NFROMFD) {
9474 fixredir(n, wordtext, 0);
9475 } else {
9476 n->nfile.fname = makename();
9477 }
9478}
Eric Andersencb57d552001-06-28 07:25:16 +00009479
Eric Andersenc470f442003-07-28 09:56:35 +00009480static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009481simplecmd(void)
9482{
9483 union node *args, **app;
9484 union node *n = NULL;
9485 union node *vars, **vpp;
9486 union node **rpp, *redir;
9487 int savecheckkwd;
9488
9489 args = NULL;
9490 app = &args;
9491 vars = NULL;
9492 vpp = &vars;
9493 redir = NULL;
9494 rpp = &redir;
9495
9496 savecheckkwd = CHKALIAS;
9497 for (;;) {
9498 checkkwd = savecheckkwd;
9499 switch (readtoken()) {
9500 case TWORD:
9501 n = stalloc(sizeof(struct narg));
9502 n->type = NARG;
9503 n->narg.text = wordtext;
9504 n->narg.backquote = backquotelist;
9505 if (savecheckkwd && isassignment(wordtext)) {
9506 *vpp = n;
9507 vpp = &n->narg.next;
9508 } else {
9509 *app = n;
9510 app = &n->narg.next;
9511 savecheckkwd = 0;
9512 }
9513 break;
9514 case TREDIR:
9515 *rpp = n = redirnode;
9516 rpp = &n->nfile.next;
9517 parsefname(); /* read name of redirection file */
9518 break;
9519 case TLP:
9520 if (args && app == &args->narg.next
9521 && !vars && !redir
9522 ) {
9523 struct builtincmd *bcmd;
9524 const char *name;
9525
9526 /* We have a function */
9527 if (readtoken() != TRP)
9528 raise_error_unexpected_syntax(TRP);
9529 name = n->narg.text;
9530 if (!goodname(name)
9531 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9532 ) {
9533 raise_error_syntax("Bad function name");
9534 }
9535 n->type = NDEFUN;
9536 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9537 n->narg.next = parse_command();
9538 return n;
9539 }
9540 /* fall through */
9541 default:
9542 tokpushback++;
9543 goto out;
9544 }
9545 }
9546 out:
9547 *app = NULL;
9548 *vpp = NULL;
9549 *rpp = NULL;
9550 n = stalloc(sizeof(struct ncmd));
9551 n->type = NCMD;
9552 n->ncmd.args = args;
9553 n->ncmd.assign = vars;
9554 n->ncmd.redirect = redir;
9555 return n;
9556}
9557
9558static union node *
9559parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009560{
Eric Andersencb57d552001-06-28 07:25:16 +00009561 union node *n1, *n2;
9562 union node *ap, **app;
9563 union node *cp, **cpp;
9564 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009565 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009566 int t;
9567
9568 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009569 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009570
Eric Andersencb57d552001-06-28 07:25:16 +00009571 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009572 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009573 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009574 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009575 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009576 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009577 n1->type = NIF;
9578 n1->nif.test = list(0);
9579 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009580 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009581 n1->nif.ifpart = list(0);
9582 n2 = n1;
9583 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009584 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009585 n2 = n2->nif.elsepart;
9586 n2->type = NIF;
9587 n2->nif.test = list(0);
9588 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009589 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009590 n2->nif.ifpart = list(0);
9591 }
9592 if (lasttoken == TELSE)
9593 n2->nif.elsepart = list(0);
9594 else {
9595 n2->nif.elsepart = NULL;
9596 tokpushback++;
9597 }
Eric Andersenc470f442003-07-28 09:56:35 +00009598 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009599 break;
9600 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009601 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009602 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009603 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009604 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009605 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009606 got = readtoken();
9607 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009608 TRACE(("expecting DO got %s %s\n", tokname(got),
9609 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009610 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009611 }
9612 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009613 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009614 break;
9615 }
9616 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009617 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009618 raise_error_syntax("Bad for loop variable");
9619 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009620 n1->type = NFOR;
9621 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009622 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009623 if (readtoken() == TIN) {
9624 app = &ap;
9625 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009626 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009627 n2->type = NARG;
9628 n2->narg.text = wordtext;
9629 n2->narg.backquote = backquotelist;
9630 *app = n2;
9631 app = &n2->narg.next;
9632 }
9633 *app = NULL;
9634 n1->nfor.args = ap;
9635 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009636 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009637 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009638 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009639 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009640 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009641 n2->narg.backquote = NULL;
9642 n2->narg.next = NULL;
9643 n1->nfor.args = n2;
9644 /*
9645 * Newline or semicolon here is optional (but note
9646 * that the original Bourne shell only allowed NL).
9647 */
9648 if (lasttoken != TNL && lasttoken != TSEMI)
9649 tokpushback++;
9650 }
Eric Andersenc470f442003-07-28 09:56:35 +00009651 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009652 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009653 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009654 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009655 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009656 break;
9657 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009658 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009659 n1->type = NCASE;
9660 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009661 raise_error_unexpected_syntax(TWORD);
9662 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009663 n2->type = NARG;
9664 n2->narg.text = wordtext;
9665 n2->narg.backquote = backquotelist;
9666 n2->narg.next = NULL;
9667 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009668 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009669 } while (readtoken() == TNL);
9670 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009671 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009672 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009673 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009674 checkkwd = CHKNL | CHKKWD;
9675 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009676 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009677 if (lasttoken == TLP)
9678 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009679 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009680 cp->type = NCLIST;
9681 app = &cp->nclist.pattern;
9682 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009683 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009684 ap->type = NARG;
9685 ap->narg.text = wordtext;
9686 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009687 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009688 break;
9689 app = &ap->narg.next;
9690 readtoken();
9691 }
9692 ap->narg.next = NULL;
9693 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009694 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009695 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009696
Eric Andersenc470f442003-07-28 09:56:35 +00009697 cpp = &cp->nclist.next;
9698
9699 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009700 t = readtoken();
9701 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009702 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009703 raise_error_unexpected_syntax(TENDCASE);
9704 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009705 }
Eric Andersenc470f442003-07-28 09:56:35 +00009706 }
Eric Andersencb57d552001-06-28 07:25:16 +00009707 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009708 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009709 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009710 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009711 n1->type = NSUBSHELL;
9712 n1->nredir.n = list(0);
9713 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009714 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009715 break;
9716 case TBEGIN:
9717 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009718 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009719 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009720 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009721 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009722 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009723 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009724 }
9725
Eric Andersenc470f442003-07-28 09:56:35 +00009726 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009727 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009728
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009729 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009730 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009731 checkkwd = CHKKWD | CHKALIAS;
9732 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009733 while (readtoken() == TREDIR) {
9734 *rpp = n2 = redirnode;
9735 rpp = &n2->nfile.next;
9736 parsefname();
9737 }
9738 tokpushback++;
9739 *rpp = NULL;
9740 if (redir) {
9741 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009742 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009743 n2->type = NREDIR;
9744 n2->nredir.n = n1;
9745 n1 = n2;
9746 }
9747 n1->nredir.redirect = redir;
9748 }
Eric Andersencb57d552001-06-28 07:25:16 +00009749 return n1;
9750}
9751
Eric Andersencb57d552001-06-28 07:25:16 +00009752/*
9753 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9754 * is not NULL, read a here document. In the latter case, eofmark is the
9755 * word which marks the end of the document and striptabs is true if
9756 * leading tabs should be stripped from the document. The argument firstc
9757 * is the first character of the input token or document.
9758 *
9759 * Because C does not have internal subroutines, I have simulated them
9760 * using goto's to implement the subroutine linkage. The following macros
9761 * will run code that appears at the end of readtoken1.
9762 */
9763
Eric Andersen2870d962001-07-02 17:27:21 +00009764#define CHECKEND() {goto checkend; checkend_return:;}
9765#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9766#define PARSESUB() {goto parsesub; parsesub_return:;}
9767#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9768#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9769#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009770
9771static int
Eric Andersenc470f442003-07-28 09:56:35 +00009772readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009773{
Eric Andersencb57d552001-06-28 07:25:16 +00009774 int c = firstc;
9775 char *out;
9776 int len;
9777 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009778 struct nodelist *bqlist = 0;
9779 int quotef = 0;
9780 int dblquote = 0;
9781 int varnest = 0; /* levels of variables expansion */
9782 int arinest = 0; /* levels of arithmetic expansion */
9783 int parenlevel = 0; /* levels of parens in arithmetic */
9784 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9785 int oldstyle = 0;
9786 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009787#if __GNUC__
9788 /* Avoid longjmp clobbering */
9789 (void) &out;
9790 (void) &quotef;
9791 (void) &dblquote;
9792 (void) &varnest;
9793 (void) &arinest;
9794 (void) &parenlevel;
9795 (void) &dqvarnest;
9796 (void) &oldstyle;
9797 (void) &prevsyntax;
9798 (void) &syntax;
9799#endif
9800
9801 startlinno = plinno;
9802 dblquote = 0;
9803 if (syntax == DQSYNTAX)
9804 dblquote = 1;
9805 quotef = 0;
9806 bqlist = NULL;
9807 varnest = 0;
9808 arinest = 0;
9809 parenlevel = 0;
9810 dqvarnest = 0;
9811
9812 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009813 loop: { /* for each line, until end of word */
9814 CHECKEND(); /* set c to PEOF if at end of here document */
9815 for (;;) { /* until end of line or end of word */
9816 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009817 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009818 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009819 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009820 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009821 USTPUTC(c, out);
9822 plinno++;
9823 if (doprompt)
9824 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009825 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009826 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009827 case CWORD:
9828 USTPUTC(c, out);
9829 break;
9830 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009831 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009832 USTPUTC(CTLESC, out);
9833 USTPUTC(c, out);
9834 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009835 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009836 c = pgetc2();
9837 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009838 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009839 USTPUTC('\\', out);
9840 pungetc();
9841 } else if (c == '\n') {
9842 if (doprompt)
9843 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009844 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009845 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009846 c != '\\' && c != '`' &&
9847 c != '$' && (
9848 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009849 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009850 ) {
9851 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009852 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009853 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009854 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009855 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009856 USTPUTC(c, out);
9857 quotef++;
9858 }
9859 break;
9860 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009861 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009862 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009863 if (eofmark == NULL) {
9864 USTPUTC(CTLQUOTEMARK, out);
9865 }
Eric Andersencb57d552001-06-28 07:25:16 +00009866 break;
9867 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009868 syntax = DQSYNTAX;
9869 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009870 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009871 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009872 if (eofmark != NULL && arinest == 0
9873 && varnest == 0
9874 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009875 USTPUTC(c, out);
9876 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009877 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009878 syntax = BASESYNTAX;
9879 dblquote = 0;
9880 }
9881 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +00009882 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009883 }
9884 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009885 case CVAR: /* '$' */
9886 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +00009887 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009888 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +00009889 if (varnest > 0) {
9890 varnest--;
9891 if (dqvarnest > 0) {
9892 dqvarnest--;
9893 }
9894 USTPUTC(CTLENDVAR, out);
9895 } else {
9896 USTPUTC(c, out);
9897 }
9898 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009899#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00009900 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009901 parenlevel++;
9902 USTPUTC(c, out);
9903 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009904 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009905 if (parenlevel > 0) {
9906 USTPUTC(c, out);
9907 --parenlevel;
9908 } else {
9909 if (pgetc() == ')') {
9910 if (--arinest == 0) {
9911 USTPUTC(CTLENDARI, out);
9912 syntax = prevsyntax;
9913 if (syntax == DQSYNTAX)
9914 dblquote = 1;
9915 else
9916 dblquote = 0;
9917 } else
9918 USTPUTC(')', out);
9919 } else {
9920 /*
9921 * unbalanced parens
9922 * (don't 2nd guess - no error)
9923 */
9924 pungetc();
9925 USTPUTC(')', out);
9926 }
9927 }
9928 break;
9929#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009930 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +00009931 PARSEBACKQOLD();
9932 break;
Eric Andersen2870d962001-07-02 17:27:21 +00009933 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009934 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009935 case CIGN:
9936 break;
9937 default:
9938 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009939 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +00009940#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +00009941 if (c != PEOA)
9942#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009943 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +00009944
Eric Andersencb57d552001-06-28 07:25:16 +00009945 }
9946 c = pgetc_macro();
9947 }
9948 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009949 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +00009950#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +00009951 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009952 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +00009953#endif
9954 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009955 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +00009956 if (varnest != 0) {
9957 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +00009958 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009959 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +00009960 }
9961 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009962 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +00009963 out = stackblock();
9964 if (eofmark == NULL) {
9965 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +00009966 && quotef == 0
9967 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009968 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +00009969 PARSEREDIR();
9970 return lasttoken = TREDIR;
9971 } else {
9972 pungetc();
9973 }
9974 }
9975 quoteflag = quotef;
9976 backquotelist = bqlist;
9977 grabstackblock(len);
9978 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009979 lasttoken = TWORD;
9980 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +00009981/* end of readtoken routine */
9982
9983
Eric Andersencb57d552001-06-28 07:25:16 +00009984/*
9985 * Check to see whether we are at the end of the here document. When this
9986 * is called, c is set to the first character of the next input line. If
9987 * we are at the end of the here document, this routine sets the c to PEOF.
9988 */
Eric Andersenc470f442003-07-28 09:56:35 +00009989checkend: {
9990 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009991#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00009992 if (c == PEOA) {
9993 c = pgetc2();
9994 }
9995#endif
9996 if (striptabs) {
9997 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009998 c = pgetc2();
9999 }
Eric Andersenc470f442003-07-28 09:56:35 +000010000 }
10001 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010002 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010003 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010004
Eric Andersenc470f442003-07-28 09:56:35 +000010005 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010006 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010007 if (*p == '\n' && *q == '\0') {
10008 c = PEOF;
10009 plinno++;
10010 needprompt = doprompt;
10011 } else {
10012 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010013 }
10014 }
10015 }
10016 }
Eric Andersenc470f442003-07-28 09:56:35 +000010017 goto checkend_return;
10018}
Eric Andersencb57d552001-06-28 07:25:16 +000010019
10020
10021/*
10022 * Parse a redirection operator. The variable "out" points to a string
10023 * specifying the fd to be redirected. The variable "c" contains the
10024 * first character of the redirection operator.
10025 */
Eric Andersenc470f442003-07-28 09:56:35 +000010026parseredir: {
10027 char fd = *out;
10028 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010029
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010030 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010031 if (c == '>') {
10032 np->nfile.fd = 1;
10033 c = pgetc();
10034 if (c == '>')
10035 np->type = NAPPEND;
10036 else if (c == '|')
10037 np->type = NCLOBBER;
10038 else if (c == '&')
10039 np->type = NTOFD;
10040 else {
10041 np->type = NTO;
10042 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010043 }
Eric Andersenc470f442003-07-28 09:56:35 +000010044 } else { /* c == '<' */
10045 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010046 c = pgetc();
10047 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010048 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010049 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010050 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010051 np->nfile.fd = 0;
10052 }
10053 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010054 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010055 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010056 c = pgetc();
10057 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010058 heredoc->striptabs = 1;
10059 } else {
10060 heredoc->striptabs = 0;
10061 pungetc();
10062 }
10063 break;
10064
10065 case '&':
10066 np->type = NFROMFD;
10067 break;
10068
10069 case '>':
10070 np->type = NFROMTO;
10071 break;
10072
10073 default:
10074 np->type = NFROM;
10075 pungetc();
10076 break;
10077 }
Eric Andersencb57d552001-06-28 07:25:16 +000010078 }
Eric Andersenc470f442003-07-28 09:56:35 +000010079 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010080 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010081 redirnode = np;
10082 goto parseredir_return;
10083}
Eric Andersencb57d552001-06-28 07:25:16 +000010084
10085
10086/*
10087 * Parse a substitution. At this point, we have read the dollar sign
10088 * and nothing else.
10089 */
Eric Andersenc470f442003-07-28 09:56:35 +000010090parsesub: {
10091 int subtype;
10092 int typeloc;
10093 int flags;
10094 char *p;
10095 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010096
Eric Andersenc470f442003-07-28 09:56:35 +000010097 c = pgetc();
10098 if (
10099 c <= PEOA_OR_PEOF ||
10100 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10101 ) {
10102 USTPUTC('$', out);
10103 pungetc();
10104 } else if (c == '(') { /* $(command) or $((arith)) */
10105 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010106#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010107 PARSEARITH();
10108#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010109 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010110#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010111 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010112 pungetc();
10113 PARSEBACKQNEW();
10114 }
10115 } else {
10116 USTPUTC(CTLVAR, out);
10117 typeloc = out - (char *)stackblock();
10118 USTPUTC(VSNORMAL, out);
10119 subtype = VSNORMAL;
10120 if (c == '{') {
10121 c = pgetc();
10122 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010123 c = pgetc();
10124 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010125 c = '#';
10126 else
10127 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010128 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010129 subtype = 0;
10130 }
10131 if (c > PEOA_OR_PEOF && is_name(c)) {
10132 do {
10133 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010134 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010135 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010136 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010137 do {
10138 STPUTC(c, out);
10139 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010140 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010141 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010142 USTPUTC(c, out);
10143 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010144 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010145 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010146
Eric Andersenc470f442003-07-28 09:56:35 +000010147 STPUTC('=', out);
10148 flags = 0;
10149 if (subtype == 0) {
10150 switch (c) {
10151 case ':':
10152 flags = VSNUL;
10153 c = pgetc();
10154 /*FALLTHROUGH*/
10155 default:
10156 p = strchr(types, c);
10157 if (p == NULL)
10158 goto badsub;
10159 subtype = p - types + VSNORMAL;
10160 break;
10161 case '%':
10162 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010163 {
10164 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010165 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010166 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010167 c = pgetc();
10168 if (c == cc)
10169 subtype++;
10170 else
10171 pungetc();
10172 break;
10173 }
10174 }
Eric Andersenc470f442003-07-28 09:56:35 +000010175 } else {
10176 pungetc();
10177 }
10178 if (dblquote || arinest)
10179 flags |= VSQUOTE;
10180 *((char *)stackblock() + typeloc) = subtype | flags;
10181 if (subtype != VSNORMAL) {
10182 varnest++;
10183 if (dblquote || arinest) {
10184 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010185 }
10186 }
10187 }
Eric Andersenc470f442003-07-28 09:56:35 +000010188 goto parsesub_return;
10189}
Eric Andersencb57d552001-06-28 07:25:16 +000010190
10191
10192/*
10193 * Called to parse command substitutions. Newstyle is set if the command
10194 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10195 * list of commands (passed by reference), and savelen is the number of
10196 * characters on the top of the stack which must be preserved.
10197 */
Eric Andersenc470f442003-07-28 09:56:35 +000010198parsebackq: {
10199 struct nodelist **nlpp;
10200 int savepbq;
10201 union node *n;
10202 char *volatile str;
10203 struct jmploc jmploc;
10204 struct jmploc *volatile savehandler;
10205 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010206 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010207#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010208 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010209#endif
10210
Eric Andersenc470f442003-07-28 09:56:35 +000010211 savepbq = parsebackquote;
10212 if (setjmp(jmploc.loc)) {
10213 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010214 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010215 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010216 exception_handler = savehandler;
10217 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010218 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010219 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010220 str = NULL;
10221 savelen = out - (char *)stackblock();
10222 if (savelen > 0) {
10223 str = ckmalloc(savelen);
10224 memcpy(str, stackblock(), savelen);
10225 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010226 savehandler = exception_handler;
10227 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010228 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010229 if (oldstyle) {
10230 /* We must read until the closing backquote, giving special
10231 treatment to some slashes, and then push the string and
10232 reread it as input, interpreting it normally. */
10233 char *pout;
10234 int pc;
10235 size_t psavelen;
10236 char *pstr;
10237
10238
10239 STARTSTACKSTR(pout);
10240 for (;;) {
10241 if (needprompt) {
10242 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010243 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010244 pc = pgetc();
10245 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010246 case '`':
10247 goto done;
10248
10249 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010250 pc = pgetc();
10251 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010252 plinno++;
10253 if (doprompt)
10254 setprompt(2);
10255 /*
10256 * If eating a newline, avoid putting
10257 * the newline into the new character
10258 * stream (via the STPUTC after the
10259 * switch).
10260 */
10261 continue;
10262 }
10263 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010264 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010265 STPUTC('\\', pout);
10266 if (pc > PEOA_OR_PEOF) {
10267 break;
10268 }
10269 /* fall through */
10270
10271 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010272#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010273 case PEOA:
10274#endif
10275 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010276 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010277
10278 case '\n':
10279 plinno++;
10280 needprompt = doprompt;
10281 break;
10282
10283 default:
10284 break;
10285 }
10286 STPUTC(pc, pout);
10287 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010288 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010289 STPUTC('\0', pout);
10290 psavelen = pout - (char *)stackblock();
10291 if (psavelen > 0) {
10292 pstr = grabstackstr(pout);
10293 setinputstring(pstr);
10294 }
10295 }
10296 nlpp = &bqlist;
10297 while (*nlpp)
10298 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010299 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010300 (*nlpp)->next = NULL;
10301 parsebackquote = oldstyle;
10302
10303 if (oldstyle) {
10304 saveprompt = doprompt;
10305 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010306 }
10307
Eric Andersenc470f442003-07-28 09:56:35 +000010308 n = list(2);
10309
10310 if (oldstyle)
10311 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010312 else if (readtoken() != TRP)
10313 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010314
10315 (*nlpp)->n = n;
10316 if (oldstyle) {
10317 /*
10318 * Start reading from old file again, ignoring any pushed back
10319 * tokens left from the backquote parsing
10320 */
10321 popfile();
10322 tokpushback = 0;
10323 }
10324 while (stackblocksize() <= savelen)
10325 growstackblock();
10326 STARTSTACKSTR(out);
10327 if (str) {
10328 memcpy(out, str, savelen);
10329 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010330 INT_OFF;
10331 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010332 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010333 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010334 }
10335 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010336 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010337 if (arinest || dblquote)
10338 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10339 else
10340 USTPUTC(CTLBACKQ, out);
10341 if (oldstyle)
10342 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010343 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010344}
10345
Denis Vlasenko131ae172007-02-18 13:00:19 +000010346#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010347/*
10348 * Parse an arithmetic expansion (indicate start of one and set state)
10349 */
Eric Andersenc470f442003-07-28 09:56:35 +000010350parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010351 if (++arinest == 1) {
10352 prevsyntax = syntax;
10353 syntax = ARISYNTAX;
10354 USTPUTC(CTLARI, out);
10355 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010356 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010357 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010358 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010359 } else {
10360 /*
10361 * we collapse embedded arithmetic expansion to
10362 * parenthesis, which should be equivalent
10363 */
10364 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010365 }
Eric Andersenc470f442003-07-28 09:56:35 +000010366 goto parsearith_return;
10367}
10368#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010369
Eric Andersenc470f442003-07-28 09:56:35 +000010370} /* end of readtoken */
10371
Eric Andersencb57d552001-06-28 07:25:16 +000010372/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010373 * Read the next input token.
10374 * If the token is a word, we set backquotelist to the list of cmds in
10375 * backquotes. We set quoteflag to true if any part of the word was
10376 * quoted.
10377 * If the token is TREDIR, then we set redirnode to a structure containing
10378 * the redirection.
10379 * In all cases, the variable startlinno is set to the number of the line
10380 * on which the token starts.
10381 *
10382 * [Change comment: here documents and internal procedures]
10383 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10384 * word parsing code into a separate routine. In this case, readtoken
10385 * doesn't need to have any internal procedures, but parseword does.
10386 * We could also make parseoperator in essence the main routine, and
10387 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010388 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010389#define NEW_xxreadtoken
10390#ifdef NEW_xxreadtoken
10391/* singles must be first! */
10392static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010393
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010394static const char xxreadtoken_tokens[] = {
10395 TNL, TLP, TRP, /* only single occurrence allowed */
10396 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10397 TEOF, /* corresponds to trailing nul */
10398 TAND, TOR, TENDCASE, /* if double occurrence */
10399};
10400
10401#define xxreadtoken_doubles \
10402 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10403#define xxreadtoken_singles \
10404 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10405
10406static int
10407xxreadtoken(void)
10408{
10409 int c;
10410
10411 if (tokpushback) {
10412 tokpushback = 0;
10413 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010414 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010415 if (needprompt) {
10416 setprompt(2);
10417 }
10418 startlinno = plinno;
10419 for (;;) { /* until token or start of word found */
10420 c = pgetc_macro();
10421
10422 if ((c != ' ') && (c != '\t')
10423#if ENABLE_ASH_ALIAS
10424 && (c != PEOA)
10425#endif
10426 ) {
10427 if (c == '#') {
10428 while ((c = pgetc()) != '\n' && c != PEOF);
10429 pungetc();
10430 } else if (c == '\\') {
10431 if (pgetc() != '\n') {
10432 pungetc();
10433 goto READTOKEN1;
10434 }
10435 startlinno = ++plinno;
10436 if (doprompt)
10437 setprompt(2);
10438 } else {
10439 const char *p
10440 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10441
10442 if (c != PEOF) {
10443 if (c == '\n') {
10444 plinno++;
10445 needprompt = doprompt;
10446 }
10447
10448 p = strchr(xxreadtoken_chars, c);
10449 if (p == NULL) {
10450 READTOKEN1:
10451 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10452 }
10453
10454 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10455 if (pgetc() == *p) { /* double occurrence? */
10456 p += xxreadtoken_doubles + 1;
10457 } else {
10458 pungetc();
10459 }
10460 }
10461 }
10462 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10463 }
10464 }
10465 } /* for */
10466}
10467#else
10468#define RETURN(token) return lasttoken = token
10469static int
10470xxreadtoken(void)
10471{
10472 int c;
10473
10474 if (tokpushback) {
10475 tokpushback = 0;
10476 return lasttoken;
10477 }
10478 if (needprompt) {
10479 setprompt(2);
10480 }
10481 startlinno = plinno;
10482 for (;;) { /* until token or start of word found */
10483 c = pgetc_macro();
10484 switch (c) {
10485 case ' ': case '\t':
10486#if ENABLE_ASH_ALIAS
10487 case PEOA:
10488#endif
10489 continue;
10490 case '#':
10491 while ((c = pgetc()) != '\n' && c != PEOF);
10492 pungetc();
10493 continue;
10494 case '\\':
10495 if (pgetc() == '\n') {
10496 startlinno = ++plinno;
10497 if (doprompt)
10498 setprompt(2);
10499 continue;
10500 }
10501 pungetc();
10502 goto breakloop;
10503 case '\n':
10504 plinno++;
10505 needprompt = doprompt;
10506 RETURN(TNL);
10507 case PEOF:
10508 RETURN(TEOF);
10509 case '&':
10510 if (pgetc() == '&')
10511 RETURN(TAND);
10512 pungetc();
10513 RETURN(TBACKGND);
10514 case '|':
10515 if (pgetc() == '|')
10516 RETURN(TOR);
10517 pungetc();
10518 RETURN(TPIPE);
10519 case ';':
10520 if (pgetc() == ';')
10521 RETURN(TENDCASE);
10522 pungetc();
10523 RETURN(TSEMI);
10524 case '(':
10525 RETURN(TLP);
10526 case ')':
10527 RETURN(TRP);
10528 default:
10529 goto breakloop;
10530 }
10531 }
10532 breakloop:
10533 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10534#undef RETURN
10535}
10536#endif /* NEW_xxreadtoken */
10537
10538static int
10539readtoken(void)
10540{
10541 int t;
10542#if DEBUG
10543 int alreadyseen = tokpushback;
10544#endif
10545
10546#if ENABLE_ASH_ALIAS
10547 top:
10548#endif
10549
10550 t = xxreadtoken();
10551
10552 /*
10553 * eat newlines
10554 */
10555 if (checkkwd & CHKNL) {
10556 while (t == TNL) {
10557 parseheredoc();
10558 t = xxreadtoken();
10559 }
10560 }
10561
10562 if (t != TWORD || quoteflag) {
10563 goto out;
10564 }
10565
10566 /*
10567 * check for keywords
10568 */
10569 if (checkkwd & CHKKWD) {
10570 const char *const *pp;
10571
10572 pp = findkwd(wordtext);
10573 if (pp) {
10574 lasttoken = t = pp - tokname_array;
10575 TRACE(("keyword %s recognized\n", tokname(t)));
10576 goto out;
10577 }
10578 }
10579
10580 if (checkkwd & CHKALIAS) {
10581#if ENABLE_ASH_ALIAS
10582 struct alias *ap;
10583 ap = lookupalias(wordtext, 1);
10584 if (ap != NULL) {
10585 if (*ap->val) {
10586 pushstring(ap->val, ap);
10587 }
10588 goto top;
10589 }
10590#endif
10591 }
10592 out:
10593 checkkwd = 0;
10594#if DEBUG
10595 if (!alreadyseen)
10596 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10597 else
10598 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10599#endif
10600 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010601}
10602
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010603static char
10604peektoken(void)
10605{
10606 int t;
10607
10608 t = readtoken();
10609 tokpushback++;
10610 return tokname_array[t][0];
10611}
Eric Andersencb57d552001-06-28 07:25:16 +000010612
10613/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010614 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10615 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010616 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010617static union node *
10618parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010619{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010620 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010621
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010622 tokpushback = 0;
10623 doprompt = interact;
10624 if (doprompt)
10625 setprompt(doprompt);
10626 needprompt = 0;
10627 t = readtoken();
10628 if (t == TEOF)
10629 return NEOF;
10630 if (t == TNL)
10631 return NULL;
10632 tokpushback++;
10633 return list(1);
10634}
10635
10636/*
10637 * Input any here documents.
10638 */
10639static void
10640parseheredoc(void)
10641{
10642 struct heredoc *here;
10643 union node *n;
10644
10645 here = heredoclist;
10646 heredoclist = 0;
10647
10648 while (here) {
10649 if (needprompt) {
10650 setprompt(2);
10651 }
10652 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10653 here->eofmark, here->striptabs);
10654 n = stalloc(sizeof(struct narg));
10655 n->narg.type = NARG;
10656 n->narg.next = NULL;
10657 n->narg.text = wordtext;
10658 n->narg.backquote = backquotelist;
10659 here->here->nhere.doc = n;
10660 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010661 }
Eric Andersencb57d552001-06-28 07:25:16 +000010662}
10663
10664
10665/*
Eric Andersencb57d552001-06-28 07:25:16 +000010666 * called by editline -- any expansions to the prompt
10667 * should be added here.
10668 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010669#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010670static const char *
10671expandstr(const char *ps)
10672{
10673 union node n;
10674
10675 /* XXX Fix (char *) cast. */
10676 setinputstring((char *)ps);
10677 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10678 popfile();
10679
10680 n.narg.type = NARG;
10681 n.narg.next = NULL;
10682 n.narg.text = wordtext;
10683 n.narg.backquote = backquotelist;
10684
10685 expandarg(&n, NULL, 0);
10686 return stackblock();
10687}
10688#endif
10689
Eric Andersencb57d552001-06-28 07:25:16 +000010690
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010691/*
10692 * Execute a command or commands contained in a string.
10693 */
10694static int
10695evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010696{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010697 union node *n;
10698 struct stackmark smark;
10699 int skip;
10700
10701 setinputstring(s);
10702 setstackmark(&smark);
10703
10704 skip = 0;
10705 while ((n = parsecmd(0)) != NEOF) {
10706 evaltree(n, 0);
10707 popstackmark(&smark);
10708 skip = evalskip;
10709 if (skip)
10710 break;
10711 }
10712 popfile();
10713
10714 skip &= mask;
10715 evalskip = skip;
10716 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010717}
10718
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010719/*
10720 * The eval command.
10721 */
10722static int
10723evalcmd(int argc, char **argv)
10724{
10725 char *p;
10726 char *concat;
10727 char **ap;
10728
10729 if (argc > 1) {
10730 p = argv[1];
10731 if (argc > 2) {
10732 STARTSTACKSTR(concat);
10733 ap = argv + 2;
10734 for (;;) {
10735 concat = stack_putstr(p, concat);
10736 p = *ap++;
10737 if (p == NULL)
10738 break;
10739 STPUTC(' ', concat);
10740 }
10741 STPUTC('\0', concat);
10742 p = grabstackstr(concat);
10743 }
10744 evalstring(p, ~SKIPEVAL);
10745
10746 }
10747 return exitstatus;
10748}
10749
10750/*
10751 * Read and execute commands. "Top" is nonzero for the top level command
10752 * loop; it turns on prompting if the shell is interactive.
10753 */
10754static int
10755cmdloop(int top)
10756{
10757 union node *n;
10758 struct stackmark smark;
10759 int inter;
10760 int numeof = 0;
10761
10762 TRACE(("cmdloop(%d) called\n", top));
10763 for (;;) {
10764 int skip;
10765
10766 setstackmark(&smark);
10767#if JOBS
10768 if (jobctl)
10769 showjobs(stderr, SHOW_CHANGED);
10770#endif
10771 inter = 0;
10772 if (iflag && top) {
10773 inter++;
10774#if ENABLE_ASH_MAIL
10775 chkmail();
10776#endif
10777 }
10778 n = parsecmd(inter);
10779 /* showtree(n); DEBUG */
10780 if (n == NEOF) {
10781 if (!top || numeof >= 50)
10782 break;
10783 if (!stoppedjobs()) {
10784 if (!Iflag)
10785 break;
10786 out2str("\nUse \"exit\" to leave shell.\n");
10787 }
10788 numeof++;
10789 } else if (nflag == 0) {
10790 job_warning = (job_warning == 2) ? 1 : 0;
10791 numeof = 0;
10792 evaltree(n, 0);
10793 }
10794 popstackmark(&smark);
10795 skip = evalskip;
10796
10797 if (skip) {
10798 evalskip = 0;
10799 return skip & SKIPEVAL;
10800 }
10801 }
10802 return 0;
10803}
10804
10805static int
10806dotcmd(int argc, char **argv)
10807{
10808 struct strlist *sp;
10809 volatile struct shparam saveparam;
10810 int status = 0;
10811
10812 for (sp = cmdenviron; sp; sp = sp->next)
10813 setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
10814
10815 if (argc >= 2) { /* That's what SVR2 does */
10816 char *fullname;
10817
10818 fullname = find_dot_file(argv[1]);
10819
10820 if (argc > 2) {
10821 saveparam = shellparam;
10822 shellparam.malloc = 0;
10823 shellparam.nparam = argc - 2;
10824 shellparam.p = argv + 2;
10825 };
10826
10827 setinputfile(fullname, INPUT_PUSH_FILE);
10828 commandname = fullname;
10829 cmdloop(0);
10830 popfile();
10831
10832 if (argc > 2) {
10833 freeparam(&shellparam);
10834 shellparam = saveparam;
10835 };
10836 status = exitstatus;
10837 }
10838 return status;
10839}
10840
10841static int
10842exitcmd(int argc, char **argv)
10843{
10844 if (stoppedjobs())
10845 return 0;
10846 if (argc > 1)
10847 exitstatus = number(argv[1]);
10848 raise_exception(EXEXIT);
10849 /* NOTREACHED */
10850}
10851
10852#if ENABLE_ASH_BUILTIN_ECHO
10853static int
10854echocmd(int argc, char **argv)
10855{
10856 return bb_echo(argv);
10857}
10858#endif
10859
10860#if ENABLE_ASH_BUILTIN_TEST
10861static int
10862testcmd(int argc, char **argv)
10863{
10864 return bb_test(argc, argv);
10865}
10866#endif
10867
10868/*
10869 * Read a file containing shell functions.
10870 */
10871static void
10872readcmdfile(char *name)
10873{
10874 setinputfile(name, INPUT_PUSH_FILE);
10875 cmdloop(0);
10876 popfile();
10877}
10878
10879
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010880/* redir.c */
Eric Andersenc470f442003-07-28 09:56:35 +000010881
Eric Andersencb57d552001-06-28 07:25:16 +000010882/*
10883 * Code for dealing with input/output redirection.
10884 */
10885
Eric Andersenc470f442003-07-28 09:56:35 +000010886#define EMPTY -2 /* marks an unused slot in redirtab */
Eric Andersencb57d552001-06-28 07:25:16 +000010887#ifndef PIPE_BUF
Eric Andersenc470f442003-07-28 09:56:35 +000010888# define PIPESIZE 4096 /* amount of buffering in a pipe */
Eric Andersencb57d552001-06-28 07:25:16 +000010889#else
10890# define PIPESIZE PIPE_BUF
10891#endif
10892
Eric Andersen62483552001-07-10 06:09:16 +000010893/*
10894 * Open a file in noclobber mode.
10895 * The code was copied from bash.
10896 */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010897static int
10898noclobberopen(const char *fname)
Eric Andersen62483552001-07-10 06:09:16 +000010899{
10900 int r, fd;
10901 struct stat finfo, finfo2;
10902
10903 /*
10904 * If the file exists and is a regular file, return an error
10905 * immediately.
10906 */
10907 r = stat(fname, &finfo);
10908 if (r == 0 && S_ISREG(finfo.st_mode)) {
10909 errno = EEXIST;
10910 return -1;
10911 }
10912
10913 /*
10914 * If the file was not present (r != 0), make sure we open it
10915 * exclusively so that if it is created before we open it, our open
10916 * will fail. Make sure that we do not truncate an existing file.
10917 * Note that we don't turn on O_EXCL unless the stat failed -- if the
10918 * file was not a regular file, we leave O_EXCL off.
10919 */
10920 if (r != 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010921 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
10922 fd = open(fname, O_WRONLY|O_CREAT, 0666);
Eric Andersen62483552001-07-10 06:09:16 +000010923
10924 /* If the open failed, return the file descriptor right away. */
10925 if (fd < 0)
10926 return fd;
10927
10928 /*
10929 * OK, the open succeeded, but the file may have been changed from a
10930 * non-regular file to a regular file between the stat and the open.
10931 * We are assuming that the O_EXCL open handles the case where FILENAME
10932 * did not exist and is symlinked to an existing file between the stat
10933 * and open.
10934 */
10935
10936 /*
10937 * If we can open it and fstat the file descriptor, and neither check
10938 * revealed that it was a regular file, and the file has not been
10939 * replaced, return the file descriptor.
10940 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010941 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
10942 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
Eric Andersen62483552001-07-10 06:09:16 +000010943 return fd;
10944
10945 /* The file has been replaced. badness. */
10946 close(fd);
10947 errno = EEXIST;
10948 return -1;
10949}
Eric Andersencb57d552001-06-28 07:25:16 +000010950
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010951
Eric Andersencb57d552001-06-28 07:25:16 +000010952/*
Eric Andersen62483552001-07-10 06:09:16 +000010953 * Handle here documents. Normally we fork off a process to write the
10954 * data to a pipe. If the document is short, we can stuff the data in
10955 * the pipe without forking.
Eric Andersencb57d552001-06-28 07:25:16 +000010956 */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010957static int
10958openhere(union node *redir)
Eric Andersen62483552001-07-10 06:09:16 +000010959{
10960 int pip[2];
Eric Andersenc470f442003-07-28 09:56:35 +000010961 size_t len = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010962
Eric Andersen62483552001-07-10 06:09:16 +000010963 if (pipe(pip) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010964 ash_msg_and_raise_error("Pipe call failed");
Eric Andersen62483552001-07-10 06:09:16 +000010965 if (redir->type == NHERE) {
10966 len = strlen(redir->nhere.doc->narg.text);
10967 if (len <= PIPESIZE) {
Rob Landley53437472006-07-16 08:14:35 +000010968 full_write(pip[1], redir->nhere.doc->narg.text, len);
Eric Andersen62483552001-07-10 06:09:16 +000010969 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010970 }
Eric Andersencb57d552001-06-28 07:25:16 +000010971 }
Eric Andersenc470f442003-07-28 09:56:35 +000010972 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Eric Andersen62483552001-07-10 06:09:16 +000010973 close(pip[0]);
10974 signal(SIGINT, SIG_IGN);
10975 signal(SIGQUIT, SIG_IGN);
10976 signal(SIGHUP, SIG_IGN);
10977#ifdef SIGTSTP
10978 signal(SIGTSTP, SIG_IGN);
10979#endif
10980 signal(SIGPIPE, SIG_DFL);
10981 if (redir->type == NHERE)
Rob Landley53437472006-07-16 08:14:35 +000010982 full_write(pip[1], redir->nhere.doc->narg.text, len);
Eric Andersen62483552001-07-10 06:09:16 +000010983 else
10984 expandhere(redir->nhere.doc, pip[1]);
10985 _exit(0);
10986 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010987 out:
Eric Andersen62483552001-07-10 06:09:16 +000010988 close(pip[1]);
10989 return pip[0];
Eric Andersencb57d552001-06-28 07:25:16 +000010990}
10991
Eric Andersenc470f442003-07-28 09:56:35 +000010992static int
10993openredirect(union node *redir)
Eric Andersen62483552001-07-10 06:09:16 +000010994{
Eric Andersencb57d552001-06-28 07:25:16 +000010995 char *fname;
10996 int f;
10997
10998 switch (redir->nfile.type) {
10999 case NFROM:
11000 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011001 f = open(fname, O_RDONLY);
11002 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000011003 goto eopen;
11004 break;
11005 case NFROMTO:
11006 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011007 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
11008 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000011009 goto ecreate;
11010 break;
11011 case NTO:
11012 /* Take care of noclobber mode. */
11013 if (Cflag) {
11014 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011015 f = noclobberopen(fname);
11016 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000011017 goto ecreate;
11018 break;
11019 }
Eric Andersenc470f442003-07-28 09:56:35 +000011020 /* FALLTHROUGH */
11021 case NCLOBBER:
Eric Andersencb57d552001-06-28 07:25:16 +000011022 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011023 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
11024 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000011025 goto ecreate;
Eric Andersencb57d552001-06-28 07:25:16 +000011026 break;
11027 case NAPPEND:
11028 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011029 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
11030 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000011031 goto ecreate;
Eric Andersencb57d552001-06-28 07:25:16 +000011032 break;
11033 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011034#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011035 abort();
11036#endif
11037 /* Fall through to eliminate warning. */
11038 case NTOFD:
11039 case NFROMFD:
11040 f = -1;
11041 break;
11042 case NHERE:
11043 case NXHERE:
11044 f = openhere(redir);
11045 break;
11046 }
11047
11048 return f;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011049 ecreate:
Denis Vlasenkob012b102007-02-19 22:43:01 +000011050 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011051 eopen:
Denis Vlasenkob012b102007-02-19 22:43:01 +000011052 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
Eric Andersencb57d552001-06-28 07:25:16 +000011053}
11054
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011055static void
11056dupredirect(union node *redir, int f)
Eric Andersenc470f442003-07-28 09:56:35 +000011057{
11058 int fd = redir->nfile.fd;
11059
11060 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
11061 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011062 copyfd(redir->ndup.dupfd, fd);
Eric Andersenc470f442003-07-28 09:56:35 +000011063 }
11064 return;
11065 }
11066
11067 if (f != fd) {
11068 copyfd(f, fd);
11069 close(f);
11070 }
Eric Andersenc470f442003-07-28 09:56:35 +000011071}
Eric Andersencb57d552001-06-28 07:25:16 +000011072
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011073
Eric Andersen62483552001-07-10 06:09:16 +000011074/*
11075 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
11076 * old file descriptors are stashed away so that the redirection can be
11077 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
11078 * standard output, and the standard error if it becomes a duplicate of
Eric Andersenc470f442003-07-28 09:56:35 +000011079 * stdout, is saved in memory.
Eric Andersen62483552001-07-10 06:09:16 +000011080 */
Eric Andersenc470f442003-07-28 09:56:35 +000011081static void
11082redirect(union node *redir, int flags)
Eric Andersen62483552001-07-10 06:09:16 +000011083{
11084 union node *n;
Eric Andersenc470f442003-07-28 09:56:35 +000011085 struct redirtab *sv;
Glenn L McGrath50812ff2002-08-23 13:14:48 +000011086 int i;
Eric Andersen62483552001-07-10 06:09:16 +000011087 int fd;
11088 int newfd;
Eric Andersenc470f442003-07-28 09:56:35 +000011089 int *p;
11090 nullredirs++;
11091 if (!redir) {
11092 return;
Eric Andersen62483552001-07-10 06:09:16 +000011093 }
Eric Andersenc470f442003-07-28 09:56:35 +000011094 sv = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011095 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011096 if (flags & REDIR_PUSH) {
11097 struct redirtab *q;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011098 q = ckmalloc(sizeof(struct redirtab));
Eric Andersenc470f442003-07-28 09:56:35 +000011099 q->next = redirlist;
11100 redirlist = q;
11101 q->nullredirs = nullredirs - 1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011102 for (i = 0; i < 10; i++)
Eric Andersenc470f442003-07-28 09:56:35 +000011103 q->renamed[i] = EMPTY;
11104 nullredirs = 0;
11105 sv = q;
11106 }
11107 n = redir;
11108 do {
Eric Andersen62483552001-07-10 06:09:16 +000011109 fd = n->nfile.fd;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011110 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
11111 && n->ndup.dupfd == fd)
Eric Andersenc470f442003-07-28 09:56:35 +000011112 continue; /* redirect from/to same file descriptor */
Eric Andersen62483552001-07-10 06:09:16 +000011113
Eric Andersen62483552001-07-10 06:09:16 +000011114 newfd = openredirect(n);
Eric Andersenc470f442003-07-28 09:56:35 +000011115 if (fd == newfd)
11116 continue;
11117 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
11118 i = fcntl(fd, F_DUPFD, 10);
11119
11120 if (i == -1) {
11121 i = errno;
11122 if (i != EBADF) {
11123 close(newfd);
11124 errno = i;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011125 ash_msg_and_raise_error("%d: %m", fd);
Eric Andersen62483552001-07-10 06:09:16 +000011126 /* NOTREACHED */
11127 }
Eric Andersenc470f442003-07-28 09:56:35 +000011128 } else {
11129 *p = i;
Eric Andersen62483552001-07-10 06:09:16 +000011130 close(fd);
Eric Andersen62483552001-07-10 06:09:16 +000011131 }
Eric Andersenc470f442003-07-28 09:56:35 +000011132 } else {
Eric Andersen62483552001-07-10 06:09:16 +000011133 close(fd);
11134 }
Eric Andersenc470f442003-07-28 09:56:35 +000011135 dupredirect(n, newfd);
11136 } while ((n = n->nfile.next));
Denis Vlasenkob012b102007-02-19 22:43:01 +000011137 INT_ON;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000011138 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
11139 preverrout_fd = sv->renamed[2];
Eric Andersen62483552001-07-10 06:09:16 +000011140}
11141
11142
Eric Andersencb57d552001-06-28 07:25:16 +000011143/*
11144 * Undo the effects of the last redirection.
11145 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011146static void
Eric Andersenc470f442003-07-28 09:56:35 +000011147popredir(int drop)
Eric Andersen2870d962001-07-02 17:27:21 +000011148{
Eric Andersenc470f442003-07-28 09:56:35 +000011149 struct redirtab *rp;
Eric Andersencb57d552001-06-28 07:25:16 +000011150 int i;
11151
Eric Andersenc470f442003-07-28 09:56:35 +000011152 if (--nullredirs >= 0)
11153 return;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011154 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011155 rp = redirlist;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011156 for (i = 0; i < 10; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011157 if (rp->renamed[i] != EMPTY) {
Eric Andersenc470f442003-07-28 09:56:35 +000011158 if (!drop) {
11159 close(i);
11160 copyfd(rp->renamed[i], i);
Eric Andersencb57d552001-06-28 07:25:16 +000011161 }
Eric Andersenc470f442003-07-28 09:56:35 +000011162 close(rp->renamed[i]);
Eric Andersencb57d552001-06-28 07:25:16 +000011163 }
11164 }
11165 redirlist = rp->next;
Eric Andersenc470f442003-07-28 09:56:35 +000011166 nullredirs = rp->nullredirs;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011167 free(rp);
11168 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011169}
11170
11171/*
Eric Andersenc470f442003-07-28 09:56:35 +000011172 * Undo all redirections. Called on error or interrupt.
11173 */
11174
11175/*
Eric Andersencb57d552001-06-28 07:25:16 +000011176 * Discard all saved file descriptors.
11177 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011178static void
Eric Andersenc470f442003-07-28 09:56:35 +000011179clearredir(int drop)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011180{
Eric Andersenc470f442003-07-28 09:56:35 +000011181 for (;;) {
11182 nullredirs = 0;
11183 if (!redirlist)
11184 break;
11185 popredir(drop);
Eric Andersencb57d552001-06-28 07:25:16 +000011186 }
Eric Andersencb57d552001-06-28 07:25:16 +000011187}
11188
11189
Eric Andersencb57d552001-06-28 07:25:16 +000011190/*
11191 * Copy a file descriptor to be >= to. Returns -1
11192 * if the source file descriptor is closed, EMPTY if there are no unused
11193 * file descriptors left.
11194 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011195static int
Eric Andersenc470f442003-07-28 09:56:35 +000011196copyfd(int from, int to)
Eric Andersencb57d552001-06-28 07:25:16 +000011197{
11198 int newfd;
11199
11200 newfd = fcntl(from, F_DUPFD, to);
11201 if (newfd < 0) {
11202 if (errno == EMFILE)
11203 return EMPTY;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011204 ash_msg_and_raise_error("%d: %m", from);
Eric Andersencb57d552001-06-28 07:25:16 +000011205 }
11206 return newfd;
11207}
11208
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011209static int
Eric Andersenc470f442003-07-28 09:56:35 +000011210redirectsafe(union node *redir, int flags)
11211{
11212 int err;
11213 volatile int saveint;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011214 struct jmploc *volatile savehandler = exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +000011215 struct jmploc jmploc;
11216
Denis Vlasenkob012b102007-02-19 22:43:01 +000011217 SAVE_INT(saveint);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011218 err = setjmp(jmploc.loc) * 2;
11219 if (!err) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011220 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +000011221 redirect(redir, flags);
11222 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011223 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011224 if (err && exception != EXERROR)
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011225 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011226 RESTORE_INT(saveint);
Eric Andersenc470f442003-07-28 09:56:35 +000011227 return err;
11228}
11229
Eric Andersenc470f442003-07-28 09:56:35 +000011230
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011231/* trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011232
Eric Andersencb57d552001-06-28 07:25:16 +000011233/*
Eric Andersencb57d552001-06-28 07:25:16 +000011234 * The trap builtin.
11235 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011236static int
Eric Andersenc470f442003-07-28 09:56:35 +000011237trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011238{
11239 char *action;
11240 char **ap;
11241 int signo;
11242
Eric Andersenc470f442003-07-28 09:56:35 +000011243 nextopt(nullstr);
11244 ap = argptr;
11245 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011246 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011247 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011248 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011249
Rob Landleyc9c1a412006-07-12 19:17:55 +000011250 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011251 out1fmt("trap -- %s %s\n",
11252 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011253 }
11254 }
11255 return 0;
11256 }
Eric Andersenc470f442003-07-28 09:56:35 +000011257 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011258 action = NULL;
11259 else
11260 action = *ap++;
11261 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011262 signo = get_signum(*ap);
11263 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011264 ash_msg_and_raise_error("%s: bad trap", *ap);
11265 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011266 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011267 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011268 action = NULL;
11269 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011270 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011271 }
Eric Andersenc470f442003-07-28 09:56:35 +000011272 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011273 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011274 trap[signo] = action;
11275 if (signo != 0)
11276 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011277 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011278 ap++;
11279 }
11280 return 0;
11281}
11282
11283
Eric Andersenc470f442003-07-28 09:56:35 +000011284/*
11285 * Clear traps on a fork.
11286 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011287static void
Eric Andersenc470f442003-07-28 09:56:35 +000011288clear_traps(void)
11289{
11290 char **tp;
11291
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011292 for (tp = trap; tp < &trap[NSIG]; tp++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011293 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011294 INT_OFF;
11295 free(*tp);
Eric Andersenc470f442003-07-28 09:56:35 +000011296 *tp = NULL;
11297 if (tp != &trap[0])
11298 setsignal(tp - trap);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011299 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011300 }
11301 }
11302}
11303
11304
Eric Andersencb57d552001-06-28 07:25:16 +000011305/*
11306 * Set the signal handler for the specified signal. The routine figures
11307 * out what it should be set to.
11308 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011309static void
Eric Andersenc470f442003-07-28 09:56:35 +000011310setsignal(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011311{
11312 int action;
Eric Andersenc470f442003-07-28 09:56:35 +000011313 char *t, tsig;
Eric Andersencb57d552001-06-28 07:25:16 +000011314 struct sigaction act;
11315
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011316 t = trap[signo];
11317 if (t == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011318 action = S_DFL;
11319 else if (*t != '\0')
11320 action = S_CATCH;
11321 else
11322 action = S_IGN;
11323 if (rootshell && action == S_DFL) {
11324 switch (signo) {
11325 case SIGINT:
11326 if (iflag || minusc || sflag == 0)
11327 action = S_CATCH;
11328 break;
11329 case SIGQUIT:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011330#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011331 if (debug)
11332 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011333#endif
11334 /* FALLTHROUGH */
11335 case SIGTERM:
11336 if (iflag)
11337 action = S_IGN;
11338 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011339#if JOBS
Eric Andersencb57d552001-06-28 07:25:16 +000011340 case SIGTSTP:
11341 case SIGTTOU:
11342 if (mflag)
11343 action = S_IGN;
11344 break;
11345#endif
11346 }
11347 }
11348
11349 t = &sigmode[signo - 1];
Eric Andersenc470f442003-07-28 09:56:35 +000011350 tsig = *t;
11351 if (tsig == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000011352 /*
11353 * current setting unknown
11354 */
11355 if (sigaction(signo, 0, &act) == -1) {
11356 /*
11357 * Pretend it worked; maybe we should give a warning
11358 * here, but other shells don't. We don't alter
11359 * sigmode, so that we retry every time.
11360 */
11361 return;
11362 }
11363 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011364 if (mflag
11365 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011366 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011367 tsig = S_IGN; /* don't hard ignore these */
Eric Andersencb57d552001-06-28 07:25:16 +000011368 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011369 tsig = S_HARD_IGN;
Eric Andersencb57d552001-06-28 07:25:16 +000011370 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011371 tsig = S_RESET; /* force to be set */
Eric Andersencb57d552001-06-28 07:25:16 +000011372 }
11373 }
Eric Andersenc470f442003-07-28 09:56:35 +000011374 if (tsig == S_HARD_IGN || tsig == action)
Eric Andersencb57d552001-06-28 07:25:16 +000011375 return;
Eric Andersenc470f442003-07-28 09:56:35 +000011376 switch (action) {
11377 case S_CATCH:
11378 act.sa_handler = onsig;
11379 break;
11380 case S_IGN:
11381 act.sa_handler = SIG_IGN;
11382 break;
11383 default:
11384 act.sa_handler = SIG_DFL;
11385 }
Eric Andersencb57d552001-06-28 07:25:16 +000011386 *t = action;
11387 act.sa_flags = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011388 sigfillset(&act.sa_mask);
Eric Andersencb57d552001-06-28 07:25:16 +000011389 sigaction(signo, &act, 0);
11390}
11391
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011392
Eric Andersencb57d552001-06-28 07:25:16 +000011393/*
Eric Andersencb57d552001-06-28 07:25:16 +000011394 * Called to execute a trap. Perhaps we should avoid entering new trap
11395 * handlers while we are executing a trap handler.
11396 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011397static int
Eric Andersenc470f442003-07-28 09:56:35 +000011398dotrap(void)
Eric Andersen2870d962001-07-02 17:27:21 +000011399{
Eric Andersenc470f442003-07-28 09:56:35 +000011400 char *p;
11401 char *q;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011402 int i;
Eric Andersencb57d552001-06-28 07:25:16 +000011403 int savestatus;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011404 int skip = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011405
Eric Andersenc470f442003-07-28 09:56:35 +000011406 savestatus = exitstatus;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011407 pendingsigs = 0;
11408 xbarrier();
11409
11410 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
11411 if (!*q)
11412 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011413 *q = '\0';
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011414
11415 p = trap[i + 1];
Eric Andersenc470f442003-07-28 09:56:35 +000011416 if (!p)
11417 continue;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011418 skip = evalstring(p, SKIPEVAL);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011419 exitstatus = savestatus;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011420 if (skip)
11421 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011422 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011423
11424 return skip;
Eric Andersencb57d552001-06-28 07:25:16 +000011425}
11426
Eric Andersenc470f442003-07-28 09:56:35 +000011427/*
11428 * Controls whether the shell is interactive or not.
11429 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011430static void
Eric Andersenc470f442003-07-28 09:56:35 +000011431setinteractive(int on)
11432{
11433 static int is_interactive;
11434
11435 if (++on == is_interactive)
11436 return;
11437 is_interactive = on;
11438 setsignal(SIGINT);
11439 setsignal(SIGQUIT);
11440 setsignal(SIGTERM);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011441#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011442 if (is_interactive > 1) {
11443 /* Looks like they want an interactive shell */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011444 static smallint do_banner;
Eric Andersenc470f442003-07-28 09:56:35 +000011445
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011446 if (!do_banner) {
11447 out1fmt(
11448 "\n\n"
11449 "%s Built-in shell (ash)\n"
11450 "Enter 'help' for a list of built-in commands."
11451 "\n\n",
11452 BB_BANNER);
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011453 do_banner = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011454 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011455 }
Eric Andersenc470f442003-07-28 09:56:35 +000011456#endif
11457}
11458
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011459#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011460/*
11461 * Lists available builtins
11462 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011463static int
11464helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011465{
11466 int col, i;
11467
11468 out1fmt("\nBuilt-in commands:\n-------------------\n");
11469 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11470 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
11471 builtincmd[i].name + 1);
11472 if (col > 60) {
11473 out1fmt("\n");
11474 col = 0;
11475 }
11476 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011477#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011478 for (i = 0; i < NUM_APPLETS; i++) {
11479 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11480 if (col > 60) {
11481 out1fmt("\n");
11482 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011483 }
11484 }
11485#endif
11486 out1fmt("\n\n");
11487 return EXIT_SUCCESS;
11488}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011489#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011490
Eric Andersencb57d552001-06-28 07:25:16 +000011491/*
11492 * Called to exit the shell.
11493 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011494static void
Eric Andersenc470f442003-07-28 09:56:35 +000011495exitshell(void)
Eric Andersencb57d552001-06-28 07:25:16 +000011496{
Eric Andersenc470f442003-07-28 09:56:35 +000011497 struct jmploc loc;
Eric Andersencb57d552001-06-28 07:25:16 +000011498 char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011499 int status;
Eric Andersencb57d552001-06-28 07:25:16 +000011500
Eric Andersenc470f442003-07-28 09:56:35 +000011501 status = exitstatus;
11502 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011503 if (setjmp(loc.loc)) {
11504 if (exception == EXEXIT)
Denis Vlasenko7f0d7ae2007-01-18 01:12:57 +000011505/* dash bug: it just does _exit(exitstatus) here
11506 * but we have to do setjobctl(0) first!
11507 * (bug is still not fixed in dash-0.5.3 - if you run dash
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011508 * under Midnight Commander, on exit from dash MC is backgrounded) */
Denis Vlasenko7f0d7ae2007-01-18 01:12:57 +000011509 status = exitstatus;
Eric Andersenc470f442003-07-28 09:56:35 +000011510 goto out;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011511 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011512 exception_handler = &loc;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011513 p = trap[0];
11514 if (p) {
Eric Andersencb57d552001-06-28 07:25:16 +000011515 trap[0] = NULL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011516 evalstring(p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011517 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011518 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011519 out:
Denis Vlasenko7f0d7ae2007-01-18 01:12:57 +000011520 setjobctl(0);
Glenn L McGrathfdbbb042002-12-09 11:10:40 +000011521 _exit(status);
Eric Andersencb57d552001-06-28 07:25:16 +000011522 /* NOTREACHED */
11523}
11524
Eric Andersencb57d552001-06-28 07:25:16 +000011525
Eric Andersencb57d552001-06-28 07:25:16 +000011526/*
11527 * The export and readonly commands.
11528 */
Eric Andersenc470f442003-07-28 09:56:35 +000011529static int
11530exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011531{
11532 struct var *vp;
11533 char *name;
11534 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011535 char **aptr;
11536 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011537
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011538 if (nextopt("p") != 'p') {
11539 aptr = argptr;
11540 name = *aptr;
11541 if (name) {
11542 do {
11543 p = strchr(name, '=');
11544 if (p != NULL) {
11545 p++;
11546 } else {
11547 vp = *findvar(hashvar(name), name);
11548 if (vp) {
11549 vp->flags |= flag;
11550 continue;
11551 }
Eric Andersencb57d552001-06-28 07:25:16 +000011552 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011553 setvar(name, p, flag);
11554 } while ((name = *++aptr) != NULL);
11555 return 0;
11556 }
Eric Andersencb57d552001-06-28 07:25:16 +000011557 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011558 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011559 return 0;
11560}
11561
Eric Andersen34506362001-08-02 05:02:46 +000011562
Eric Andersencb57d552001-06-28 07:25:16 +000011563/*
Eric Andersencb57d552001-06-28 07:25:16 +000011564 * Make a variable a local variable. When a variable is made local, it's
11565 * value and flags are saved in a localvar structure. The saved values
11566 * will be restored when the shell function returns. We handle the name
11567 * "-" as a special case.
11568 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011569static void
11570mklocal(char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011571{
Eric Andersencb57d552001-06-28 07:25:16 +000011572 struct localvar *lvp;
11573 struct var **vpp;
11574 struct var *vp;
11575
Denis Vlasenkob012b102007-02-19 22:43:01 +000011576 INT_OFF;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011577 lvp = ckmalloc(sizeof(struct localvar));
Denis Vlasenko9f739442006-12-16 23:49:13 +000011578 if (LONE_DASH(name)) {
Eric Andersencb57d552001-06-28 07:25:16 +000011579 char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011580 p = ckmalloc(sizeof(optlist));
11581 lvp->text = memcpy(p, optlist, sizeof(optlist));
Eric Andersencb57d552001-06-28 07:25:16 +000011582 vp = NULL;
11583 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011584 char *eq;
11585
Eric Andersencb57d552001-06-28 07:25:16 +000011586 vpp = hashvar(name);
11587 vp = *findvar(vpp, name);
Eric Andersenc470f442003-07-28 09:56:35 +000011588 eq = strchr(name, '=');
Eric Andersencb57d552001-06-28 07:25:16 +000011589 if (vp == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011590 if (eq)
11591 setvareq(name, VSTRFIXED);
Eric Andersencb57d552001-06-28 07:25:16 +000011592 else
11593 setvar(name, NULL, VSTRFIXED);
Eric Andersenc470f442003-07-28 09:56:35 +000011594 vp = *vpp; /* the new variable */
Eric Andersencb57d552001-06-28 07:25:16 +000011595 lvp->flags = VUNSET;
11596 } else {
11597 lvp->text = vp->text;
11598 lvp->flags = vp->flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011599 vp->flags |= VSTRFIXED|VTEXTFIXED;
11600 if (eq)
11601 setvareq(name, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011602 }
11603 }
11604 lvp->vp = vp;
11605 lvp->next = localvars;
11606 localvars = lvp;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011607 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011608}
11609
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011610
Eric Andersenc470f442003-07-28 09:56:35 +000011611/*
11612 * The "local" command.
11613 */
Eric Andersenc470f442003-07-28 09:56:35 +000011614static int
11615localcmd(int argc, char **argv)
11616{
11617 char *name;
11618
11619 argv = argptr;
11620 while ((name = *argv++) != NULL) {
11621 mklocal(name);
11622 }
11623 return 0;
11624}
11625
11626
Eric Andersencb57d552001-06-28 07:25:16 +000011627/*
Eric Andersencb57d552001-06-28 07:25:16 +000011628 * The unset builtin command. We unset the function before we unset the
11629 * variable to allow a function to be unset when there is a readonly variable
11630 * with the same name.
11631 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011632static int
Eric Andersenc470f442003-07-28 09:56:35 +000011633unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011634{
11635 char **ap;
11636 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011637 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011638 int ret = 0;
11639
11640 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011641 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011642 }
Eric Andersencb57d552001-06-28 07:25:16 +000011643
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011644 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011645 if (flag != 'f') {
11646 i = unsetvar(*ap);
11647 ret |= i;
11648 if (!(i & 2))
11649 continue;
11650 }
11651 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011652 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011653 }
Eric Andersenc470f442003-07-28 09:56:35 +000011654 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011655}
11656
11657
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011658/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011659
Eric Andersenc470f442003-07-28 09:56:35 +000011660#include <sys/times.h>
11661
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011662static const unsigned char timescmd_str[] = {
11663 ' ', offsetof(struct tms, tms_utime),
11664 '\n', offsetof(struct tms, tms_stime),
11665 ' ', offsetof(struct tms, tms_cutime),
11666 '\n', offsetof(struct tms, tms_cstime),
11667 0
11668};
Eric Andersencb57d552001-06-28 07:25:16 +000011669
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011670static int
11671timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011672{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011673 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011674 const unsigned char *p;
11675 struct tms buf;
11676
11677 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011678 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011679
11680 p = timescmd_str;
11681 do {
11682 t = *(clock_t *)(((char *) &buf) + p[1]);
11683 s = t / clk_tck;
11684 out1fmt("%ldm%ld.%.3lds%c",
11685 s/60, s%60,
11686 ((t - s * clk_tck) * 1000) / clk_tck,
11687 p[0]);
11688 } while (*(p += 2));
11689
Eric Andersencb57d552001-06-28 07:25:16 +000011690 return 0;
11691}
11692
Denis Vlasenko131ae172007-02-18 13:00:19 +000011693#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011694static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011695dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011696{
Eric Andersened9ecf72004-06-22 08:29:45 +000011697 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011698 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011699
Denis Vlasenkob012b102007-02-19 22:43:01 +000011700 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011701 result = arith(s, &errcode);
11702 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011703 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011704 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011705 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011706 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011707 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011708 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011709 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011710 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011711 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011712
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011713 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011714}
Eric Andersenc470f442003-07-28 09:56:35 +000011715
11716
11717/*
Eric Andersen90898442003-08-06 11:20:52 +000011718 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11719 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11720 *
11721 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011722 */
11723static int
Eric Andersen90898442003-08-06 11:20:52 +000011724letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011725{
Eric Andersenc470f442003-07-28 09:56:35 +000011726 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011727 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011728
Eric Andersen90898442003-08-06 11:20:52 +000011729 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011730 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011731 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011732 for (ap = argv + 1; *ap; ap++) {
11733 i = dash_arith(*ap);
11734 }
Eric Andersenc470f442003-07-28 09:56:35 +000011735
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011736 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011737}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011738#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011739
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011740/* miscbltin.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011741
11742/*
Eric Andersenaff114c2004-04-14 17:51:38 +000011743 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011744 */
11745
11746#undef rflag
11747
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011748#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011749typedef enum __rlimit_resource rlim_t;
11750#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011751
11752
Eric Andersenc470f442003-07-28 09:56:35 +000011753/*
11754 * The read builtin. The -e option causes backslashes to escape the
11755 * following character.
11756 *
11757 * This uses unbuffered input, which may be avoidable in some cases.
11758 */
Eric Andersenc470f442003-07-28 09:56:35 +000011759static int
11760readcmd(int argc, char **argv)
11761{
11762 char **ap;
11763 int backslash;
11764 char c;
11765 int rflag;
11766 char *prompt;
11767 const char *ifs;
11768 char *p;
11769 int startword;
11770 int status;
11771 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011772#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011773 int nch_flag = 0;
11774 int nchars = 0;
11775 int silent = 0;
11776 struct termios tty, old_tty;
11777#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011778#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011779 fd_set set;
11780 struct timeval ts;
11781
11782 ts.tv_sec = ts.tv_usec = 0;
11783#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011784
11785 rflag = 0;
11786 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011787#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011788 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011789#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011790 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011791#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011792 while ((i = nextopt("p:rt:")) != '\0')
11793#else
11794 while ((i = nextopt("p:r")) != '\0')
11795#endif
11796 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011797 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011798 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011799 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011800 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011801#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011802 case 'n':
11803 nchars = strtol(optionarg, &p, 10);
11804 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011805 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011806 nch_flag = (nchars > 0);
11807 break;
11808 case 's':
11809 silent = 1;
11810 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011811#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011812#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011813 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011814 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011815 ts.tv_usec = 0;
11816 if (*p == '.') {
11817 char *p2;
11818 if (*++p) {
11819 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011820 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011821 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011822 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011823 scale = p2 - p;
11824 /* normalize to usec */
11825 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011826 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011827 while (scale++ < 6)
11828 ts.tv_usec *= 10;
11829 }
11830 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011831 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011832 }
11833 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011834 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011835 break;
11836#endif
11837 case 'r':
11838 rflag = 1;
11839 break;
11840 default:
11841 break;
11842 }
Eric Andersenc470f442003-07-28 09:56:35 +000011843 }
11844 if (prompt && isatty(0)) {
11845 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011846 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011847 ap = argptr;
11848 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011849 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011850 ifs = bltinlookup("IFS");
11851 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011852 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011853#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011854 if (nch_flag || silent) {
11855 tcgetattr(0, &tty);
11856 old_tty = tty;
11857 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011858 tty.c_lflag &= ~ICANON;
11859 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011860 }
11861 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011862 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011863
11864 }
11865 tcsetattr(0, TCSANOW, &tty);
11866 }
11867#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011868#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011869 if (ts.tv_sec || ts.tv_usec) {
11870 FD_ZERO (&set);
11871 FD_SET (0, &set);
11872
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011873 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011874 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011875#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011876 if (nch_flag)
11877 tcsetattr(0, TCSANOW, &old_tty);
11878#endif
11879 return 1;
11880 }
11881 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011882#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011883 status = 0;
11884 startword = 1;
11885 backslash = 0;
11886 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011887#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011888 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011889#else
11890 for (;;)
11891#endif
11892 {
Eric Andersenc470f442003-07-28 09:56:35 +000011893 if (read(0, &c, 1) != 1) {
11894 status = 1;
11895 break;
11896 }
11897 if (c == '\0')
11898 continue;
11899 if (backslash) {
11900 backslash = 0;
11901 if (c != '\n')
11902 goto put;
11903 continue;
11904 }
11905 if (!rflag && c == '\\') {
11906 backslash++;
11907 continue;
11908 }
11909 if (c == '\n')
11910 break;
11911 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11912 continue;
11913 }
11914 startword = 0;
11915 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11916 STACKSTRNUL(p);
11917 setvar(*ap, stackblock(), 0);
11918 ap++;
11919 startword = 1;
11920 STARTSTACKSTR(p);
11921 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011922 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011923 STPUTC(c, p);
11924 }
11925 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011926#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011927 if (nch_flag || silent)
11928 tcsetattr(0, TCSANOW, &old_tty);
11929#endif
11930
Eric Andersenc470f442003-07-28 09:56:35 +000011931 STACKSTRNUL(p);
11932 /* Remove trailing blanks */
11933 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11934 *p = '\0';
11935 setvar(*ap, stackblock(), 0);
11936 while (*++ap != NULL)
11937 setvar(*ap, nullstr, 0);
11938 return status;
11939}
11940
11941
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011942static int
11943umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011944{
11945 static const char permuser[3] = "ugo";
11946 static const char permmode[3] = "rwx";
11947 static const short int permmask[] = {
11948 S_IRUSR, S_IWUSR, S_IXUSR,
11949 S_IRGRP, S_IWGRP, S_IXGRP,
11950 S_IROTH, S_IWOTH, S_IXOTH
11951 };
11952
11953 char *ap;
11954 mode_t mask;
11955 int i;
11956 int symbolic_mode = 0;
11957
11958 while (nextopt("S") != '\0') {
11959 symbolic_mode = 1;
11960 }
11961
Denis Vlasenkob012b102007-02-19 22:43:01 +000011962 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011963 mask = umask(0);
11964 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011965 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011966
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011967 ap = *argptr;
11968 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011969 if (symbolic_mode) {
11970 char buf[18];
11971 char *p = buf;
11972
11973 for (i = 0; i < 3; i++) {
11974 int j;
11975
11976 *p++ = permuser[i];
11977 *p++ = '=';
11978 for (j = 0; j < 3; j++) {
11979 if ((mask & permmask[3 * i + j]) == 0) {
11980 *p++ = permmode[j];
11981 }
11982 }
11983 *p++ = ',';
11984 }
11985 *--p = 0;
11986 puts(buf);
11987 } else {
11988 out1fmt("%.4o\n", mask);
11989 }
11990 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011991 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011992 mask = 0;
11993 do {
11994 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011995 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011996 mask = (mask << 3) + (*ap - '0');
11997 } while (*++ap != '\0');
11998 umask(mask);
11999 } else {
12000 mask = ~mask & 0777;
12001 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012002 ash_msg_and_raise_error("Illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012003 }
12004 umask(~mask & 0777);
12005 }
12006 }
12007 return 0;
12008}
12009
12010/*
12011 * ulimit builtin
12012 *
12013 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12014 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12015 * ash by J.T. Conklin.
12016 *
12017 * Public domain.
12018 */
12019
12020struct limits {
12021 const char *name;
12022 int cmd;
12023 int factor; /* multiply by to get rlim_{cur,max} values */
12024 char option;
12025};
12026
12027static const struct limits limits[] = {
12028#ifdef RLIMIT_CPU
12029 { "time(seconds)", RLIMIT_CPU, 1, 't' },
12030#endif
12031#ifdef RLIMIT_FSIZE
12032 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
12033#endif
12034#ifdef RLIMIT_DATA
12035 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
12036#endif
12037#ifdef RLIMIT_STACK
12038 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
12039#endif
12040#ifdef RLIMIT_CORE
12041 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
12042#endif
12043#ifdef RLIMIT_RSS
12044 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
12045#endif
12046#ifdef RLIMIT_MEMLOCK
12047 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
12048#endif
12049#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000012050 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012051#endif
12052#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000012053 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012054#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012055#ifdef RLIMIT_AS
12056 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012057#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012058#ifdef RLIMIT_LOCKS
12059 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012060#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012061 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000012062};
12063
Glenn L McGrath76620622004-01-13 10:19:37 +000012064enum limtype { SOFT = 0x1, HARD = 0x2 };
12065
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012066static void
12067printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012068 const struct limits *l)
12069{
12070 rlim_t val;
12071
12072 val = limit->rlim_max;
12073 if (how & SOFT)
12074 val = limit->rlim_cur;
12075
12076 if (val == RLIM_INFINITY)
12077 out1fmt("unlimited\n");
12078 else {
12079 val /= l->factor;
12080 out1fmt("%lld\n", (long long) val);
12081 }
12082}
12083
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012084static int
Eric Andersenc470f442003-07-28 09:56:35 +000012085ulimitcmd(int argc, char **argv)
12086{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012087 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012088 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012089 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012090 const struct limits *l;
12091 int set, all = 0;
12092 int optc, what;
12093 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012094
12095 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012096 while ((optc = nextopt("HSa"
12097#ifdef RLIMIT_CPU
12098 "t"
12099#endif
12100#ifdef RLIMIT_FSIZE
12101 "f"
12102#endif
12103#ifdef RLIMIT_DATA
12104 "d"
12105#endif
12106#ifdef RLIMIT_STACK
12107 "s"
12108#endif
12109#ifdef RLIMIT_CORE
12110 "c"
12111#endif
12112#ifdef RLIMIT_RSS
12113 "m"
12114#endif
12115#ifdef RLIMIT_MEMLOCK
12116 "l"
12117#endif
12118#ifdef RLIMIT_NPROC
12119 "p"
12120#endif
12121#ifdef RLIMIT_NOFILE
12122 "n"
12123#endif
12124#ifdef RLIMIT_AS
12125 "v"
12126#endif
12127#ifdef RLIMIT_LOCKS
12128 "w"
12129#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012130 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012131 switch (optc) {
12132 case 'H':
12133 how = HARD;
12134 break;
12135 case 'S':
12136 how = SOFT;
12137 break;
12138 case 'a':
12139 all = 1;
12140 break;
12141 default:
12142 what = optc;
12143 }
12144
Glenn L McGrath76620622004-01-13 10:19:37 +000012145 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000012146 ;
Eric Andersenc470f442003-07-28 09:56:35 +000012147
12148 set = *argptr ? 1 : 0;
12149 if (set) {
12150 char *p = *argptr;
12151
12152 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012153 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012154 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012155 val = RLIM_INFINITY;
12156 else {
12157 val = (rlim_t) 0;
12158
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012159 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012160 val = (val * 10) + (long)(c - '0');
12161 if (val < (rlim_t) 0)
12162 break;
12163 }
12164 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012165 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000012166 val *= l->factor;
12167 }
12168 }
12169 if (all) {
12170 for (l = limits; l->name; l++) {
12171 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000012172 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000012173 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012174 }
12175 return 0;
12176 }
12177
12178 getrlimit(l->cmd, &limit);
12179 if (set) {
12180 if (how & HARD)
12181 limit.rlim_max = val;
12182 if (how & SOFT)
12183 limit.rlim_cur = val;
12184 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012185 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012186 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012187 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012188 }
12189 return 0;
12190}
12191
Eric Andersen90898442003-08-06 11:20:52 +000012192
Denis Vlasenko131ae172007-02-18 13:00:19 +000012193#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012194
12195/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12196
12197 Permission is hereby granted, free of charge, to any person obtaining
12198 a copy of this software and associated documentation files (the
12199 "Software"), to deal in the Software without restriction, including
12200 without limitation the rights to use, copy, modify, merge, publish,
12201 distribute, sublicense, and/or sell copies of the Software, and to
12202 permit persons to whom the Software is furnished to do so, subject to
12203 the following conditions:
12204
12205 The above copyright notice and this permission notice shall be
12206 included in all copies or substantial portions of the Software.
12207
12208 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12209 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12210 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12211 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12212 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12213 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12214 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12215*/
12216
12217/* This is my infix parser/evaluator. It is optimized for size, intended
12218 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012219 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012220 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012221 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012222 * be that which POSIX specifies for shells. */
12223
12224/* The code uses a simple two-stack algorithm. See
12225 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012226 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012227 * this is based (this code differs in that it applies operators immediately
12228 * to the stack instead of adding them to a queue to end up with an
12229 * expression). */
12230
12231/* To use the routine, call it with an expression string and error return
12232 * pointer */
12233
12234/*
12235 * Aug 24, 2001 Manuel Novoa III
12236 *
12237 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12238 *
12239 * 1) In arith_apply():
12240 * a) Cached values of *numptr and &(numptr[-1]).
12241 * b) Removed redundant test for zero denominator.
12242 *
12243 * 2) In arith():
12244 * a) Eliminated redundant code for processing operator tokens by moving
12245 * to a table-based implementation. Also folded handling of parens
12246 * into the table.
12247 * b) Combined all 3 loops which called arith_apply to reduce generated
12248 * code size at the cost of speed.
12249 *
12250 * 3) The following expressions were treated as valid by the original code:
12251 * 1() , 0! , 1 ( *3 ) .
12252 * These bugs have been fixed by internally enclosing the expression in
12253 * parens and then checking that all binary ops and right parens are
12254 * preceded by a valid expression (NUM_TOKEN).
12255 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012256 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012257 * ctype's isspace() if it is used by another busybox applet or if additional
12258 * whitespace chars should be considered. Look below the "#include"s for a
12259 * precompiler test.
12260 */
12261
12262/*
12263 * Aug 26, 2001 Manuel Novoa III
12264 *
12265 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12266 *
12267 * Merge in Aaron's comments previously posted to the busybox list,
12268 * modified slightly to take account of my changes to the code.
12269 *
12270 */
12271
12272/*
12273 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12274 *
12275 * - allow access to variable,
12276 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12277 * - realize assign syntax (VAR=expr, +=, *= etc)
12278 * - realize exponentiation (** operator)
12279 * - realize comma separated - expr, expr
12280 * - realise ++expr --expr expr++ expr--
12281 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012282 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012283 * - was restored loses XOR operator
12284 * - remove one goto label, added three ;-)
12285 * - protect $((num num)) as true zero expr (Manuel`s error)
12286 * - always use special isspace(), see comment from bash ;-)
12287 */
12288
Eric Andersen90898442003-08-06 11:20:52 +000012289#define arith_isspace(arithval) \
12290 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12291
Eric Andersen90898442003-08-06 11:20:52 +000012292typedef unsigned char operator;
12293
12294/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012295 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012296 * precedence. The ID portion is so that multiple operators can have the
12297 * same precedence, ensuring that the leftmost one is evaluated first.
12298 * Consider * and /. */
12299
12300#define tok_decl(prec,id) (((id)<<5)|(prec))
12301#define PREC(op) ((op) & 0x1F)
12302
12303#define TOK_LPAREN tok_decl(0,0)
12304
12305#define TOK_COMMA tok_decl(1,0)
12306
12307#define TOK_ASSIGN tok_decl(2,0)
12308#define TOK_AND_ASSIGN tok_decl(2,1)
12309#define TOK_OR_ASSIGN tok_decl(2,2)
12310#define TOK_XOR_ASSIGN tok_decl(2,3)
12311#define TOK_PLUS_ASSIGN tok_decl(2,4)
12312#define TOK_MINUS_ASSIGN tok_decl(2,5)
12313#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12314#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12315
12316#define TOK_MUL_ASSIGN tok_decl(3,0)
12317#define TOK_DIV_ASSIGN tok_decl(3,1)
12318#define TOK_REM_ASSIGN tok_decl(3,2)
12319
12320/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012321#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012322
12323/* conditional is right associativity too */
12324#define TOK_CONDITIONAL tok_decl(4,0)
12325#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12326
12327#define TOK_OR tok_decl(5,0)
12328
12329#define TOK_AND tok_decl(6,0)
12330
12331#define TOK_BOR tok_decl(7,0)
12332
12333#define TOK_BXOR tok_decl(8,0)
12334
12335#define TOK_BAND tok_decl(9,0)
12336
12337#define TOK_EQ tok_decl(10,0)
12338#define TOK_NE tok_decl(10,1)
12339
12340#define TOK_LT tok_decl(11,0)
12341#define TOK_GT tok_decl(11,1)
12342#define TOK_GE tok_decl(11,2)
12343#define TOK_LE tok_decl(11,3)
12344
12345#define TOK_LSHIFT tok_decl(12,0)
12346#define TOK_RSHIFT tok_decl(12,1)
12347
12348#define TOK_ADD tok_decl(13,0)
12349#define TOK_SUB tok_decl(13,1)
12350
12351#define TOK_MUL tok_decl(14,0)
12352#define TOK_DIV tok_decl(14,1)
12353#define TOK_REM tok_decl(14,2)
12354
12355/* exponent is right associativity */
12356#define TOK_EXPONENT tok_decl(15,1)
12357
12358/* For now unary operators. */
12359#define UNARYPREC 16
12360#define TOK_BNOT tok_decl(UNARYPREC,0)
12361#define TOK_NOT tok_decl(UNARYPREC,1)
12362
12363#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12364#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12365
12366#define PREC_PRE (UNARYPREC+2)
12367
12368#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12369#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12370
12371#define PREC_POST (UNARYPREC+3)
12372
12373#define TOK_POST_INC tok_decl(PREC_POST, 0)
12374#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12375
12376#define SPEC_PREC (UNARYPREC+4)
12377
12378#define TOK_NUM tok_decl(SPEC_PREC, 0)
12379#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12380
12381#define NUMPTR (*numstackptr)
12382
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012383static int
12384tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012385{
12386 operator prec = PREC(op);
12387
12388 convert_prec_is_assing(prec);
12389 return (prec == PREC(TOK_ASSIGN) ||
12390 prec == PREC_PRE || prec == PREC_POST);
12391}
12392
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012393static int
12394is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012395{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012396 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12397 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012398}
12399
Eric Andersen90898442003-08-06 11:20:52 +000012400typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012401 arith_t val;
12402 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012403 char contidional_second_val_initialized;
12404 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012405 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012406} v_n_t;
12407
Eric Andersen90898442003-08-06 11:20:52 +000012408typedef struct CHK_VAR_RECURSIVE_LOOPED {
12409 const char *var;
12410 struct CHK_VAR_RECURSIVE_LOOPED *next;
12411} chk_var_recursive_looped_t;
12412
12413static chk_var_recursive_looped_t *prev_chk_var_recursive;
12414
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012415static int
12416arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012417{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012418 if (t->var) {
12419 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012420
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012421 if (p) {
12422 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012423
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012424 /* recursive try as expression */
12425 chk_var_recursive_looped_t *cur;
12426 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012427
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012428 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12429 if (strcmp(cur->var, t->var) == 0) {
12430 /* expression recursion loop detected */
12431 return -5;
12432 }
12433 }
12434 /* save current lookuped var name */
12435 cur = prev_chk_var_recursive;
12436 cur_save.var = t->var;
12437 cur_save.next = cur;
12438 prev_chk_var_recursive = &cur_save;
12439
12440 t->val = arith (p, &errcode);
12441 /* restore previous ptr after recursiving */
12442 prev_chk_var_recursive = cur;
12443 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012444 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 /* allow undefined var as 0 */
12446 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012447 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012448 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012449}
12450
12451/* "applying" a token means performing it on the top elements on the integer
12452 * stack. For a unary operator it will only change the top element, but a
12453 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012454static int
12455arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012456{
Eric Andersen90898442003-08-06 11:20:52 +000012457 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012458 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012459 int ret_arith_lookup_val;
12460
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012461 /* There is no operator that can work without arguments */
12462 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012463 numptr_m1 = NUMPTR - 1;
12464
12465 /* check operand is var with noninteger value */
12466 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012467 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012468 return ret_arith_lookup_val;
12469
12470 rez = numptr_m1->val;
12471 if (op == TOK_UMINUS)
12472 rez *= -1;
12473 else if (op == TOK_NOT)
12474 rez = !rez;
12475 else if (op == TOK_BNOT)
12476 rez = ~rez;
12477 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12478 rez++;
12479 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12480 rez--;
12481 else if (op != TOK_UPLUS) {
12482 /* Binary operators */
12483
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012484 /* check and binary operators need two arguments */
12485 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012486
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012487 /* ... and they pop one */
12488 --NUMPTR;
12489 numptr_val = rez;
12490 if (op == TOK_CONDITIONAL) {
12491 if (! numptr_m1->contidional_second_val_initialized) {
12492 /* protect $((expr1 ? expr2)) without ": expr" */
12493 goto err;
12494 }
12495 rez = numptr_m1->contidional_second_val;
12496 } else if (numptr_m1->contidional_second_val_initialized) {
12497 /* protect $((expr1 : expr2)) without "expr ? " */
12498 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012499 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012500 numptr_m1 = NUMPTR - 1;
12501 if (op != TOK_ASSIGN) {
12502 /* check operand is var with noninteger value for not '=' */
12503 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12504 if (ret_arith_lookup_val)
12505 return ret_arith_lookup_val;
12506 }
12507 if (op == TOK_CONDITIONAL) {
12508 numptr_m1->contidional_second_val = rez;
12509 }
12510 rez = numptr_m1->val;
12511 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012512 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012513 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012514 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012515 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012516 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012517 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012518 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012519 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012520 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012521 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012522 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012523 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012524 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012525 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012526 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012527 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012528 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012529 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012530 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012531 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012532 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012533 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012534 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012535 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012536 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012537 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012538 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012539 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012540 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012541 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012542 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012543 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012544 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012545 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012546 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012547 /* protect $((expr : expr)) without "expr ? " */
12548 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012549 }
12550 numptr_m1->contidional_second_val_initialized = op;
12551 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012552 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012553 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012554 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012555 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012556 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012557 return -3; /* exponent less than 0 */
12558 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012559 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012560
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012561 if (numptr_val)
12562 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012563 c *= rez;
12564 rez = c;
12565 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012566 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012567 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012568 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012569 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012570 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012571 rez %= numptr_val;
12572 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012573 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012574 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012575
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012576 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012577 /* Hmm, 1=2 ? */
12578 goto err;
12579 }
12580 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012581#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012582 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012583#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012584 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012585#endif
Eric Andersen90898442003-08-06 11:20:52 +000012586 setvar(numptr_m1->var, buf, 0);
12587 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012588 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012589 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012590 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012591 rez++;
12592 }
12593 numptr_m1->val = rez;
12594 /* protect geting var value, is number now */
12595 numptr_m1->var = NULL;
12596 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012597 err:
12598 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012599}
12600
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012601/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012602static const char op_tokens[] = {
12603 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12604 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12605 '<','<', 0, TOK_LSHIFT,
12606 '>','>', 0, TOK_RSHIFT,
12607 '|','|', 0, TOK_OR,
12608 '&','&', 0, TOK_AND,
12609 '!','=', 0, TOK_NE,
12610 '<','=', 0, TOK_LE,
12611 '>','=', 0, TOK_GE,
12612 '=','=', 0, TOK_EQ,
12613 '|','=', 0, TOK_OR_ASSIGN,
12614 '&','=', 0, TOK_AND_ASSIGN,
12615 '*','=', 0, TOK_MUL_ASSIGN,
12616 '/','=', 0, TOK_DIV_ASSIGN,
12617 '%','=', 0, TOK_REM_ASSIGN,
12618 '+','=', 0, TOK_PLUS_ASSIGN,
12619 '-','=', 0, TOK_MINUS_ASSIGN,
12620 '-','-', 0, TOK_POST_DEC,
12621 '^','=', 0, TOK_XOR_ASSIGN,
12622 '+','+', 0, TOK_POST_INC,
12623 '*','*', 0, TOK_EXPONENT,
12624 '!', 0, TOK_NOT,
12625 '<', 0, TOK_LT,
12626 '>', 0, TOK_GT,
12627 '=', 0, TOK_ASSIGN,
12628 '|', 0, TOK_BOR,
12629 '&', 0, TOK_BAND,
12630 '*', 0, TOK_MUL,
12631 '/', 0, TOK_DIV,
12632 '%', 0, TOK_REM,
12633 '+', 0, TOK_ADD,
12634 '-', 0, TOK_SUB,
12635 '^', 0, TOK_BXOR,
12636 /* uniq */
12637 '~', 0, TOK_BNOT,
12638 ',', 0, TOK_COMMA,
12639 '?', 0, TOK_CONDITIONAL,
12640 ':', 0, TOK_CONDITIONAL_SEP,
12641 ')', 0, TOK_RPAREN,
12642 '(', 0, TOK_LPAREN,
12643 0
12644};
12645/* ptr to ")" */
12646#define endexpression &op_tokens[sizeof(op_tokens)-7]
12647
12648
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012649static arith_t
12650arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012651{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012652 char arithval; /* Current character under analysis */
12653 operator lasttok, op;
12654 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012655
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012656 const char *p = endexpression;
12657 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012658
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012659 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012660
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012661 /* Stack of integers */
12662 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12663 * in any given correct or incorrect expression is left as an exercise to
12664 * the reader. */
12665 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12666 *numstackptr = numstack;
12667 /* Stack of operator tokens */
12668 operator *stack = alloca((datasizes) * sizeof(operator)),
12669 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012670
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012671 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12672 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012673
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012674 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012675 arithval = *expr;
12676 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012677 if (p == endexpression) {
12678 /* Null expression. */
12679 return 0;
12680 }
12681
12682 /* This is only reached after all tokens have been extracted from the
12683 * input stream. If there are still tokens on the operator stack, they
12684 * are to be applied in order. At the end, there should be a final
12685 * result on the integer stack */
12686
12687 if (expr != endexpression + 1) {
12688 /* If we haven't done so already, */
12689 /* append a closing right paren */
12690 expr = endexpression;
12691 /* and let the loop process it. */
12692 continue;
12693 }
12694 /* At this point, we're done with the expression. */
12695 if (numstackptr != numstack+1) {
12696 /* ... but if there isn't, it's bad */
12697 err:
12698 return (*perrcode = -1);
12699 }
12700 if (numstack->var) {
12701 /* expression is $((var)) only, lookup now */
12702 errcode = arith_lookup_val(numstack);
12703 }
12704 ret:
12705 *perrcode = errcode;
12706 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012707 }
12708
Eric Andersen90898442003-08-06 11:20:52 +000012709 /* Continue processing the expression. */
12710 if (arith_isspace(arithval)) {
12711 /* Skip whitespace */
12712 goto prologue;
12713 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012714 p = endofname(expr);
12715 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012716 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012717
12718 numstackptr->var = alloca(var_name_size);
12719 safe_strncpy(numstackptr->var, expr, var_name_size);
12720 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012721 num:
Eric Andersen90898442003-08-06 11:20:52 +000012722 numstackptr->contidional_second_val_initialized = 0;
12723 numstackptr++;
12724 lasttok = TOK_NUM;
12725 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012726 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012727 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012728 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012729#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012730 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012731#else
12732 numstackptr->val = strtol(expr, (char **) &expr, 0);
12733#endif
Eric Andersen90898442003-08-06 11:20:52 +000012734 goto num;
12735 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012736 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012737 const char *o;
12738
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012739 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012740 /* strange operator not found */
12741 goto err;
12742 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012743 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012744 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012745 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012746 /* found */
12747 expr = o - 1;
12748 break;
12749 }
12750 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012751 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012752 p++;
12753 /* skip zero delim */
12754 p++;
12755 }
12756 op = p[1];
12757
12758 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012759 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12760 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012761
12762 /* Plus and minus are binary (not unary) _only_ if the last
12763 * token was as number, or a right paren (which pretends to be
12764 * a number, since it evaluates to one). Think about it.
12765 * It makes sense. */
12766 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012767 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012768 case TOK_ADD:
12769 op = TOK_UPLUS;
12770 break;
12771 case TOK_SUB:
12772 op = TOK_UMINUS;
12773 break;
12774 case TOK_POST_INC:
12775 op = TOK_PRE_INC;
12776 break;
12777 case TOK_POST_DEC:
12778 op = TOK_PRE_DEC;
12779 break;
Eric Andersen90898442003-08-06 11:20:52 +000012780 }
12781 }
12782 /* We don't want a unary operator to cause recursive descent on the
12783 * stack, because there can be many in a row and it could cause an
12784 * operator to be evaluated before its argument is pushed onto the
12785 * integer stack. */
12786 /* But for binary operators, "apply" everything on the operator
12787 * stack until we find an operator with a lesser priority than the
12788 * one we have just extracted. */
12789 /* Left paren is given the lowest priority so it will never be
12790 * "applied" in this way.
12791 * if associativity is right and priority eq, applied also skip
12792 */
12793 prec = PREC(op);
12794 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12795 /* not left paren or unary */
12796 if (lasttok != TOK_NUM) {
12797 /* binary op must be preceded by a num */
12798 goto err;
12799 }
12800 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012801 if (op == TOK_RPAREN) {
12802 /* The algorithm employed here is simple: while we don't
12803 * hit an open paren nor the bottom of the stack, pop
12804 * tokens and apply them */
12805 if (stackptr[-1] == TOK_LPAREN) {
12806 --stackptr;
12807 /* Any operator directly after a */
12808 lasttok = TOK_NUM;
12809 /* close paren should consider itself binary */
12810 goto prologue;
12811 }
12812 } else {
12813 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012814
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012815 convert_prec_is_assing(prec);
12816 convert_prec_is_assing(prev_prec);
12817 if (prev_prec < prec)
12818 break;
12819 /* check right assoc */
12820 if (prev_prec == prec && is_right_associativity(prec))
12821 break;
12822 }
12823 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12824 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012825 }
12826 if (op == TOK_RPAREN) {
12827 goto err;
12828 }
12829 }
12830
12831 /* Push this operator to the stack and remember it. */
12832 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012833 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012834 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012835 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012836}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012837#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012838
12839
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012840/* ============ main() and helpers
12841 *
12842 * Main routine. We initialize things, parse the arguments, execute
12843 * profiles if we're a login shell, and then call cmdloop to execute
12844 * commands. The setjmp call sets up the location to jump to when an
12845 * exception occurs. When an exception occurs the variable "state"
12846 * is used to figure out how far we had gotten.
12847 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012848static void
12849init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012850{
12851 /* from input.c: */
12852 basepf.nextc = basepf.buf = basebuf;
12853
12854 /* from trap.c: */
12855 signal(SIGCHLD, SIG_DFL);
12856
12857 /* from var.c: */
12858 {
12859 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012860 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012861 const char *p;
12862 struct stat st1, st2;
12863
12864 initvar();
12865 for (envp = environ; envp && *envp; envp++) {
12866 if (strchr(*envp, '=')) {
12867 setvareq(*envp, VEXPORT|VTEXTFIXED);
12868 }
12869 }
12870
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012871 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012872 setvar("PPID", ppid, 0);
12873
12874 p = lookupvar("PWD");
12875 if (p)
12876 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12877 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12878 p = '\0';
12879 setpwd(p, 0);
12880 }
12881}
12882
12883/*
12884 * Process the shell command line arguments.
12885 */
12886static void
12887procargs(int argc, char **argv)
12888{
12889 int i;
12890 const char *xminusc;
12891 char **xargv;
12892
12893 xargv = argv;
12894 arg0 = xargv[0];
12895 if (argc > 0)
12896 xargv++;
12897 for (i = 0; i < NOPTS; i++)
12898 optlist[i] = 2;
12899 argptr = xargv;
12900 options(1);
12901 xargv = argptr;
12902 xminusc = minusc;
12903 if (*xargv == NULL) {
12904 if (xminusc)
12905 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12906 sflag = 1;
12907 }
12908 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12909 iflag = 1;
12910 if (mflag == 2)
12911 mflag = iflag;
12912 for (i = 0; i < NOPTS; i++)
12913 if (optlist[i] == 2)
12914 optlist[i] = 0;
12915#if DEBUG == 2
12916 debug = 1;
12917#endif
12918 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12919 if (xminusc) {
12920 minusc = *xargv++;
12921 if (*xargv)
12922 goto setarg0;
12923 } else if (!sflag) {
12924 setinputfile(*xargv, 0);
12925 setarg0:
12926 arg0 = *xargv++;
12927 commandname = arg0;
12928 }
12929
12930 shellparam.p = xargv;
12931#if ENABLE_ASH_GETOPTS
12932 shellparam.optind = 1;
12933 shellparam.optoff = -1;
12934#endif
12935 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12936 while (*xargv) {
12937 shellparam.nparam++;
12938 xargv++;
12939 }
12940 optschanged();
12941}
12942
12943/*
12944 * Read /etc/profile or .profile.
12945 */
12946static void
12947read_profile(const char *name)
12948{
12949 int skip;
12950
12951 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12952 return;
12953 skip = cmdloop(0);
12954 popfile();
12955 if (skip)
12956 exitshell();
12957}
12958
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012959/*
12960 * This routine is called when an error or an interrupt occurs in an
12961 * interactive shell and control is returned to the main command loop.
12962 */
12963static void
12964reset(void)
12965{
12966 /* from eval.c: */
12967 evalskip = 0;
12968 loopnest = 0;
12969 /* from input.c: */
12970 parselleft = parsenleft = 0; /* clear input buffer */
12971 popallfiles();
12972 /* from parser.c: */
12973 tokpushback = 0;
12974 checkkwd = 0;
12975 /* from redir.c: */
12976 clearredir(0);
12977}
12978
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012979#if PROFILE
12980static short profile_buf[16384];
12981extern int etext();
12982#endif
12983
12984int ash_main(int argc, char **argv);
12985int ash_main(int argc, char **argv)
12986{
12987 char *shinit;
12988 volatile int state;
12989 struct jmploc jmploc;
12990 struct stackmark smark;
12991
12992#ifdef __GLIBC__
12993 dash_errno = __errno_location();
12994#endif
12995
12996#if PROFILE
12997 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12998#endif
12999
13000#if ENABLE_FEATURE_EDITING
13001 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13002#endif
13003 state = 0;
13004 if (setjmp(jmploc.loc)) {
13005 int e;
13006 int s;
13007
13008 reset();
13009
13010 e = exception;
13011 if (e == EXERROR)
13012 exitstatus = 2;
13013 s = state;
13014 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13015 exitshell();
13016
13017 if (e == EXINT) {
13018 outcslow('\n', stderr);
13019 }
13020 popstackmark(&smark);
13021 FORCE_INT_ON; /* enable interrupts */
13022 if (s == 1)
13023 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013024 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013025 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013026 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013027 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013028 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013029 }
13030 exception_handler = &jmploc;
13031#if DEBUG
13032 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013033 trace_puts("Shell args: ");
13034 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013035#endif
13036 rootpid = getpid();
13037
13038#if ENABLE_ASH_RANDOM_SUPPORT
13039 rseed = rootpid + time(NULL);
13040#endif
13041 init();
13042 setstackmark(&smark);
13043 procargs(argc, argv);
13044#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13045 if (iflag) {
13046 const char *hp = lookupvar("HISTFILE");
13047
13048 if (hp == NULL) {
13049 hp = lookupvar("HOME");
13050 if (hp != NULL) {
13051 char *defhp = concat_path_file(hp, ".ash_history");
13052 setvar("HISTFILE", defhp, 0);
13053 free(defhp);
13054 }
13055 }
13056 }
13057#endif
13058 if (argv[0] && argv[0][0] == '-')
13059 isloginsh = 1;
13060 if (isloginsh) {
13061 state = 1;
13062 read_profile("/etc/profile");
13063 state1:
13064 state = 2;
13065 read_profile(".profile");
13066 }
13067 state2:
13068 state = 3;
13069 if (
13070#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013071 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013072#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013073 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013074 ) {
13075 shinit = lookupvar("ENV");
13076 if (shinit != NULL && *shinit != '\0') {
13077 read_profile(shinit);
13078 }
13079 }
13080 state3:
13081 state = 4;
13082 if (minusc)
13083 evalstring(minusc, 0);
13084
13085 if (sflag || minusc == NULL) {
13086#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13087 if ( iflag ) {
13088 const char *hp = lookupvar("HISTFILE");
13089
13090 if (hp != NULL)
13091 line_input_state->hist_file = hp;
13092 }
13093#endif
13094 state4: /* XXX ??? - why isn't this before the "if" statement */
13095 cmdloop(1);
13096 }
13097#if PROFILE
13098 monitor(0);
13099#endif
13100#ifdef GPROF
13101 {
13102 extern void _mcleanup(void);
13103 _mcleanup();
13104 }
13105#endif
13106 exitshell();
13107 /* NOTREACHED */
13108}
13109
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013110#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013111const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013112int main(int argc, char **argv)
13113{
13114 return ash_main(argc, argv);
13115}
13116#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013117
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013118
Eric Andersendf82f612001-06-28 07:46:40 +000013119/*-
13120 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013121 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013122 *
13123 * This code is derived from software contributed to Berkeley by
13124 * Kenneth Almquist.
13125 *
13126 * Redistribution and use in source and binary forms, with or without
13127 * modification, are permitted provided that the following conditions
13128 * are met:
13129 * 1. Redistributions of source code must retain the above copyright
13130 * notice, this list of conditions and the following disclaimer.
13131 * 2. Redistributions in binary form must reproduce the above copyright
13132 * notice, this list of conditions and the following disclaimer in the
13133 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013134 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013135 * may be used to endorse or promote products derived from this software
13136 * without specific prior written permission.
13137 *
13138 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13139 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13140 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13141 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13142 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13143 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13144 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13145 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13146 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13147 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13148 * SUCH DAMAGE.
13149 */