blob: 29156c199366b5dbdf053d342a6b77a5054a892b [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
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000069/* ============ Misc helpers */
70
71#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
72
73/* C99 say: "char" declaration may be signed or unsigned default */
74#define signed_char2int(sc) ((int)((signed char)sc))
75
76
Denis Vlasenkob012b102007-02-19 22:43:01 +000077/* ============ Shell options */
78
79static const char *const optletters_optnames[] = {
80 "e" "errexit",
81 "f" "noglob",
82 "I" "ignoreeof",
83 "i" "interactive",
84 "m" "monitor",
85 "n" "noexec",
86 "s" "stdin",
87 "x" "xtrace",
88 "v" "verbose",
89 "C" "noclobber",
90 "a" "allexport",
91 "b" "notify",
92 "u" "nounset",
93 "\0" "vi",
94#if DEBUG
95 "\0" "nolog",
96 "\0" "debug",
97#endif
98};
99
100#define optletters(n) optletters_optnames[(n)][0]
101#define optnames(n) (&optletters_optnames[(n)][1])
102
103#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
104
105static char optlist[NOPTS];
106
107#define eflag optlist[0]
108#define fflag optlist[1]
109#define Iflag optlist[2]
110#define iflag optlist[3]
111#define mflag optlist[4]
112#define nflag optlist[5]
113#define sflag optlist[6]
114#define xflag optlist[7]
115#define vflag optlist[8]
116#define Cflag optlist[9]
117#define aflag optlist[10]
118#define bflag optlist[11]
119#define uflag optlist[12]
120#define viflag optlist[13]
121#if DEBUG
122#define nolog optlist[14]
123#define debug optlist[15]
124#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000125
126
Denis Vlasenkob012b102007-02-19 22:43:01 +0000127/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000128
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000129#ifdef __GLIBC__
130/* glibc sucks */
131static int *dash_errno;
132#undef errno
133#define errno (*dash_errno)
134#endif
135
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000136static char nullstr[1]; /* zero length string */
137static const char homestr[] = "HOME";
138static const char snlfmt[] = "%s\n";
139static const char illnum[] = "Illegal number: %s";
140
Denis Vlasenkocc571512007-02-23 21:10:35 +0000141static char *minusc; /* argument to -c option */
142
Denis Vlasenkoa624c112007-02-19 22:45:43 +0000143static int isloginsh;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000144/* pid of main shell */
145static int rootpid;
146/* shell level: 0 for the main shell, 1 for its children, and so on */
147static int shlvl;
148#define rootshell (!shlvl)
149/* trap handler commands */
150static char *trap[NSIG];
151/* current value of signal */
152static char sigmode[NSIG - 1];
153/* indicates specified signal received */
154static char gotsig[NSIG - 1];
155static char *arg0; /* value of $0 */
Eric Andersenc470f442003-07-28 09:56:35 +0000156
157
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000158/* ============ Interrupts / exceptions */
159
160/*
Eric Andersenc470f442003-07-28 09:56:35 +0000161 * We enclose jmp_buf in a structure so that we can declare pointers to
162 * jump locations. The global variable handler contains the location to
163 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000164 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000165 * exception handlers, the user should save the value of handler on entry
166 * to an inner scope, set handler to point to a jmploc structure for the
167 * inner scope, and restore handler on exit from the scope.
168 */
Eric Andersenc470f442003-07-28 09:56:35 +0000169struct jmploc {
170 jmp_buf loc;
171};
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000172static struct jmploc *exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +0000173static int exception;
Eric Andersenc470f442003-07-28 09:56:35 +0000174/* exceptions */
175#define EXINT 0 /* SIGINT received */
176#define EXERROR 1 /* a generic error */
177#define EXSHELLPROC 2 /* execute a shell procedure */
178#define EXEXEC 3 /* command execution failed */
179#define EXEXIT 4 /* exit the shell */
180#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000181static volatile int suppressint;
182static volatile sig_atomic_t intpending;
Eric Andersenc470f442003-07-28 09:56:35 +0000183/* do we generate EXSIG events */
184static int exsig;
185/* last pending signal */
186static volatile sig_atomic_t pendingsigs;
Eric Andersen2870d962001-07-02 17:27:21 +0000187
188/*
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000189 * Sigmode records the current value of the signal handlers for the various
190 * modes. A value of zero means that the current handler is not known.
191 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
192 */
193
194#define S_DFL 1 /* default signal handling (SIG_DFL) */
195#define S_CATCH 2 /* signal is caught */
196#define S_IGN 3 /* signal is ignored (SIG_IGN) */
197#define S_HARD_IGN 4 /* signal is ignored permenantly */
198#define S_RESET 5 /* temporary - to reset a hard ignored sig */
199
200/*
Eric Andersen2870d962001-07-02 17:27:21 +0000201 * These macros allow the user to suspend the handling of interrupt signals
202 * over a period of time. This is similar to SIGHOLD to or sigblock, but
203 * much more efficient and portable. (But hacking the kernel is so much
204 * more fun than worrying about efficiency and portability. :-))
205 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000206#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000207 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000208 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000209 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000210 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000211
212/*
213 * Called to raise an exception. Since C doesn't include exceptions, we
214 * just do a longjmp to the exception handler. The type of exception is
215 * stored in the global variable "exception".
216 */
217static void raise_exception(int) ATTRIBUTE_NORETURN;
218static void
219raise_exception(int e)
220{
221#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000222 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000223 abort();
224#endif
225 INT_OFF;
226 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000227 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000228}
229
230/*
231 * Called from trap.c when a SIGINT is received. (If the user specifies
232 * that SIGINT is to be trapped or ignored using the trap builtin, then
233 * this routine is not called.) Suppressint is nonzero when interrupts
234 * are held using the INT_OFF macro. (The test for iflag is just
235 * defensive programming.)
236 */
237static void raise_interrupt(void) ATTRIBUTE_NORETURN;
238static void
239raise_interrupt(void)
240{
241 int i;
242
243 intpending = 0;
244 i = EXSIG;
245 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
246 if (!(rootshell && iflag)) {
247 signal(SIGINT, SIG_DFL);
248 raise(SIGINT);
249 }
250 i = EXINT;
251 }
252 raise_exception(i);
253 /* NOTREACHED */
254}
255
256#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000257static void
258int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000259{
260 if (--suppressint == 0 && intpending) {
261 raise_interrupt();
262 }
263}
264#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000265static void
266force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000267{
268 suppressint = 0;
269 if (intpending)
270 raise_interrupt();
271}
272#define FORCE_INT_ON force_int_on()
273#else
274#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000275 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000276 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000277 if (--suppressint == 0 && intpending) \
278 raise_interrupt(); \
279 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000280#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000281 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000282 xbarrier(); \
283 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000284 if (intpending) \
285 raise_interrupt(); \
286 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000287#endif /* ASH_OPTIMIZE_FOR_SIZE */
288
289#define SAVE_INT(v) ((v) = suppressint)
290
291#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000292 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000293 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000294 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000295 if (suppressint == 0 && intpending) \
296 raise_interrupt(); \
297 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000298
299#define EXSIGON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000300 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000301 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000302 xbarrier(); \
Eric Andersenc470f442003-07-28 09:56:35 +0000303 if (pendingsigs) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000304 raise_exception(EXSIG); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000305 } while (0)
Eric Andersenc470f442003-07-28 09:56:35 +0000306/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000307
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000308/*
309 * Ignore a signal. Only one usage site - in forkchild()
310 */
311static void
312ignoresig(int signo)
313{
314 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
315 signal(signo, SIG_IGN);
316 }
317 sigmode[signo - 1] = S_HARD_IGN;
318}
319
320/*
321 * Signal handler. Only one usage site - in setsignal()
322 */
323static void
324onsig(int signo)
325{
326 gotsig[signo - 1] = 1;
327 pendingsigs = signo;
328
329 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
330 if (!suppressint)
331 raise_interrupt();
332 intpending = 1;
333 }
334}
335
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000336
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000337/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000338
Eric Andersenc470f442003-07-28 09:56:35 +0000339static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000340outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000341{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000342 INT_OFF;
343 fputs(p, file);
344 INT_ON;
345}
346
347static void
348flush_stdout_stderr(void)
349{
350 INT_OFF;
351 fflush(stdout);
352 fflush(stderr);
353 INT_ON;
354}
355
356static void
357flush_stderr(void)
358{
359 INT_OFF;
360 fflush(stderr);
361 INT_ON;
362}
363
364static void
365outcslow(int c, FILE *dest)
366{
367 INT_OFF;
368 putc(c, dest);
369 fflush(dest);
370 INT_ON;
371}
372
373static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
374static int
375out1fmt(const char *fmt, ...)
376{
377 va_list ap;
378 int r;
379
380 INT_OFF;
381 va_start(ap, fmt);
382 r = vprintf(fmt, ap);
383 va_end(ap);
384 INT_ON;
385 return r;
386}
387
388static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
389static int
390fmtstr(char *outbuf, size_t length, const char *fmt, ...)
391{
392 va_list ap;
393 int ret;
394
395 va_start(ap, fmt);
396 INT_OFF;
397 ret = vsnprintf(outbuf, length, fmt, ap);
398 va_end(ap);
399 INT_ON;
400 return ret;
401}
402
403static void
404out1str(const char *p)
405{
406 outstr(p, stdout);
407}
408
409static void
410out2str(const char *p)
411{
412 outstr(p, stderr);
413 flush_stderr();
414}
415
416
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000417/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000418
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000419/* control characters in argument strings */
420#define CTLESC '\201' /* escape next character */
421#define CTLVAR '\202' /* variable defn */
422#define CTLENDVAR '\203'
423#define CTLBACKQ '\204'
424#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
425/* CTLBACKQ | CTLQUOTE == '\205' */
426#define CTLARI '\206' /* arithmetic expression */
427#define CTLENDARI '\207'
428#define CTLQUOTEMARK '\210'
429
430/* variable substitution byte (follows CTLVAR) */
431#define VSTYPE 0x0f /* type of variable substitution */
432#define VSNUL 0x10 /* colon--treat the empty string as unset */
433#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
434
435/* values of VSTYPE field */
436#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
437#define VSMINUS 0x2 /* ${var-text} */
438#define VSPLUS 0x3 /* ${var+text} */
439#define VSQUESTION 0x4 /* ${var?message} */
440#define VSASSIGN 0x5 /* ${var=text} */
441#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
442#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
443#define VSTRIMLEFT 0x8 /* ${var#pattern} */
444#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
445#define VSLENGTH 0xa /* ${#var} */
446
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000447static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
448
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000449#define NCMD 0
450#define NPIPE 1
451#define NREDIR 2
452#define NBACKGND 3
453#define NSUBSHELL 4
454#define NAND 5
455#define NOR 6
456#define NSEMI 7
457#define NIF 8
458#define NWHILE 9
459#define NUNTIL 10
460#define NFOR 11
461#define NCASE 12
462#define NCLIST 13
463#define NDEFUN 14
464#define NARG 15
465#define NTO 16
466#define NCLOBBER 17
467#define NFROM 18
468#define NFROMTO 19
469#define NAPPEND 20
470#define NTOFD 21
471#define NFROMFD 22
472#define NHERE 23
473#define NXHERE 24
474#define NNOT 25
475
476union node;
477
478struct ncmd {
479 int type;
480 union node *assign;
481 union node *args;
482 union node *redirect;
483};
484
485struct npipe {
486 int type;
487 int backgnd;
488 struct nodelist *cmdlist;
489};
490
491struct nredir {
492 int type;
493 union node *n;
494 union node *redirect;
495};
496
497struct nbinary {
498 int type;
499 union node *ch1;
500 union node *ch2;
501};
502
503struct nif {
504 int type;
505 union node *test;
506 union node *ifpart;
507 union node *elsepart;
508};
509
510struct nfor {
511 int type;
512 union node *args;
513 union node *body;
514 char *var;
515};
516
517struct ncase {
518 int type;
519 union node *expr;
520 union node *cases;
521};
522
523struct nclist {
524 int type;
525 union node *next;
526 union node *pattern;
527 union node *body;
528};
529
530struct narg {
531 int type;
532 union node *next;
533 char *text;
534 struct nodelist *backquote;
535};
536
537struct nfile {
538 int type;
539 union node *next;
540 int fd;
541 union node *fname;
542 char *expfname;
543};
544
545struct ndup {
546 int type;
547 union node *next;
548 int fd;
549 int dupfd;
550 union node *vname;
551};
552
553struct nhere {
554 int type;
555 union node *next;
556 int fd;
557 union node *doc;
558};
559
560struct nnot {
561 int type;
562 union node *com;
563};
564
565union node {
566 int type;
567 struct ncmd ncmd;
568 struct npipe npipe;
569 struct nredir nredir;
570 struct nbinary nbinary;
571 struct nif nif;
572 struct nfor nfor;
573 struct ncase ncase;
574 struct nclist nclist;
575 struct narg narg;
576 struct nfile nfile;
577 struct ndup ndup;
578 struct nhere nhere;
579 struct nnot nnot;
580};
581
582struct nodelist {
583 struct nodelist *next;
584 union node *n;
585};
586
587struct funcnode {
588 int count;
589 union node n;
590};
591
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000592/*
593 * Free a parse tree.
594 */
595static void
596freefunc(struct funcnode *f)
597{
598 if (f && --f->count < 0)
599 free(f);
600}
601
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000602
603/* ============ Debugging output */
604
605#if DEBUG
606
607static FILE *tracefile;
608
609static void
610trace_printf(const char *fmt, ...)
611{
612 va_list va;
613
614 if (debug != 1)
615 return;
616 va_start(va, fmt);
617 vfprintf(tracefile, fmt, va);
618 va_end(va);
619}
620
621static void
622trace_vprintf(const char *fmt, va_list va)
623{
624 if (debug != 1)
625 return;
626 vfprintf(tracefile, fmt, va);
627}
628
629static void
630trace_puts(const char *s)
631{
632 if (debug != 1)
633 return;
634 fputs(s, tracefile);
635}
636
637static void
638trace_puts_quoted(char *s)
639{
640 char *p;
641 char c;
642
643 if (debug != 1)
644 return;
645 putc('"', tracefile);
646 for (p = s; *p; p++) {
647 switch (*p) {
648 case '\n': c = 'n'; goto backslash;
649 case '\t': c = 't'; goto backslash;
650 case '\r': c = 'r'; goto backslash;
651 case '"': c = '"'; goto backslash;
652 case '\\': c = '\\'; goto backslash;
653 case CTLESC: c = 'e'; goto backslash;
654 case CTLVAR: c = 'v'; goto backslash;
655 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
656 case CTLBACKQ: c = 'q'; goto backslash;
657 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
658 backslash:
659 putc('\\', tracefile);
660 putc(c, tracefile);
661 break;
662 default:
663 if (*p >= ' ' && *p <= '~')
664 putc(*p, tracefile);
665 else {
666 putc('\\', tracefile);
667 putc(*p >> 6 & 03, tracefile);
668 putc(*p >> 3 & 07, tracefile);
669 putc(*p & 07, tracefile);
670 }
671 break;
672 }
673 }
674 putc('"', tracefile);
675}
676
677static void
678trace_puts_args(char **ap)
679{
680 if (debug != 1)
681 return;
682 if (!*ap)
683 return;
684 while (1) {
685 trace_puts_quoted(*ap);
686 if (!*++ap) {
687 putc('\n', tracefile);
688 break;
689 }
690 putc(' ', tracefile);
691 }
692}
693
694static void
695opentrace(void)
696{
697 char s[100];
698#ifdef O_APPEND
699 int flags;
700#endif
701
702 if (debug != 1) {
703 if (tracefile)
704 fflush(tracefile);
705 /* leave open because libedit might be using it */
706 return;
707 }
708 strcpy(s, "./trace");
709 if (tracefile) {
710 if (!freopen(s, "a", tracefile)) {
711 fprintf(stderr, "Can't re-open %s\n", s);
712 debug = 0;
713 return;
714 }
715 } else {
716 tracefile = fopen(s, "a");
717 if (tracefile == NULL) {
718 fprintf(stderr, "Can't open %s\n", s);
719 debug = 0;
720 return;
721 }
722 }
723#ifdef O_APPEND
724 flags = fcntl(fileno(tracefile), F_GETFL, 0);
725 if (flags >= 0)
726 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
727#endif
728 setlinebuf(tracefile);
729 fputs("\nTracing started.\n", tracefile);
730}
731
732static void
733indent(int amount, char *pfx, FILE *fp)
734{
735 int i;
736
737 for (i = 0; i < amount; i++) {
738 if (pfx && i == amount - 1)
739 fputs(pfx, fp);
740 putc('\t', fp);
741 }
742}
743
744/* little circular references here... */
745static void shtree(union node *n, int ind, char *pfx, FILE *fp);
746
747static void
748sharg(union node *arg, FILE *fp)
749{
750 char *p;
751 struct nodelist *bqlist;
752 int subtype;
753
754 if (arg->type != NARG) {
755 out1fmt("<node type %d>\n", arg->type);
756 abort();
757 }
758 bqlist = arg->narg.backquote;
759 for (p = arg->narg.text; *p; p++) {
760 switch (*p) {
761 case CTLESC:
762 putc(*++p, fp);
763 break;
764 case CTLVAR:
765 putc('$', fp);
766 putc('{', fp);
767 subtype = *++p;
768 if (subtype == VSLENGTH)
769 putc('#', fp);
770
771 while (*p != '=')
772 putc(*p++, fp);
773
774 if (subtype & VSNUL)
775 putc(':', fp);
776
777 switch (subtype & VSTYPE) {
778 case VSNORMAL:
779 putc('}', fp);
780 break;
781 case VSMINUS:
782 putc('-', fp);
783 break;
784 case VSPLUS:
785 putc('+', fp);
786 break;
787 case VSQUESTION:
788 putc('?', fp);
789 break;
790 case VSASSIGN:
791 putc('=', fp);
792 break;
793 case VSTRIMLEFT:
794 putc('#', fp);
795 break;
796 case VSTRIMLEFTMAX:
797 putc('#', fp);
798 putc('#', fp);
799 break;
800 case VSTRIMRIGHT:
801 putc('%', fp);
802 break;
803 case VSTRIMRIGHTMAX:
804 putc('%', fp);
805 putc('%', fp);
806 break;
807 case VSLENGTH:
808 break;
809 default:
810 out1fmt("<subtype %d>", subtype);
811 }
812 break;
813 case CTLENDVAR:
814 putc('}', fp);
815 break;
816 case CTLBACKQ:
817 case CTLBACKQ|CTLQUOTE:
818 putc('$', fp);
819 putc('(', fp);
820 shtree(bqlist->n, -1, NULL, fp);
821 putc(')', fp);
822 break;
823 default:
824 putc(*p, fp);
825 break;
826 }
827 }
828}
829
830static void
831shcmd(union node *cmd, FILE *fp)
832{
833 union node *np;
834 int first;
835 const char *s;
836 int dftfd;
837
838 first = 1;
839 for (np = cmd->ncmd.args; np; np = np->narg.next) {
840 if (! first)
841 putchar(' ');
842 sharg(np, fp);
843 first = 0;
844 }
845 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
846 if (! first)
847 putchar(' ');
848 switch (np->nfile.type) {
849 case NTO: s = ">"; dftfd = 1; break;
850 case NCLOBBER: s = ">|"; dftfd = 1; break;
851 case NAPPEND: s = ">>"; dftfd = 1; break;
852 case NTOFD: s = ">&"; dftfd = 1; break;
853 case NFROM: s = "<"; dftfd = 0; break;
854 case NFROMFD: s = "<&"; dftfd = 0; break;
855 case NFROMTO: s = "<>"; dftfd = 0; break;
856 default: s = "*error*"; dftfd = 0; break;
857 }
858 if (np->nfile.fd != dftfd)
859 fprintf(fp, "%d", np->nfile.fd);
860 fputs(s, fp);
861 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
862 fprintf(fp, "%d", np->ndup.dupfd);
863 } else {
864 sharg(np->nfile.fname, fp);
865 }
866 first = 0;
867 }
868}
869
870static void
871shtree(union node *n, int ind, char *pfx, FILE *fp)
872{
873 struct nodelist *lp;
874 const char *s;
875
876 if (n == NULL)
877 return;
878
879 indent(ind, pfx, fp);
880 switch (n->type) {
881 case NSEMI:
882 s = "; ";
883 goto binop;
884 case NAND:
885 s = " && ";
886 goto binop;
887 case NOR:
888 s = " || ";
889 binop:
890 shtree(n->nbinary.ch1, ind, NULL, fp);
891 /* if (ind < 0) */
892 fputs(s, fp);
893 shtree(n->nbinary.ch2, ind, NULL, fp);
894 break;
895 case NCMD:
896 shcmd(n, fp);
897 if (ind >= 0)
898 putc('\n', fp);
899 break;
900 case NPIPE:
901 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
902 shcmd(lp->n, fp);
903 if (lp->next)
904 fputs(" | ", fp);
905 }
906 if (n->npipe.backgnd)
907 fputs(" &", fp);
908 if (ind >= 0)
909 putc('\n', fp);
910 break;
911 default:
912 fprintf(fp, "<node type %d>", n->type);
913 if (ind >= 0)
914 putc('\n', fp);
915 break;
916 }
917}
918
919static void
920showtree(union node *n)
921{
922 trace_puts("showtree called\n");
923 shtree(n, 1, NULL, stdout);
924}
925
926#define TRACE(param) trace_printf param
927#define TRACEV(param) trace_vprintf param
928
929#else
930
931#define TRACE(param)
932#define TRACEV(param)
933
934#endif /* DEBUG */
935
936
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000937/* ============ Parser data */
938
939/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000940 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
941 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000942struct strlist {
943 struct strlist *next;
944 char *text;
945};
946
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000947#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000948struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000949#endif
950
Denis Vlasenkob012b102007-02-19 22:43:01 +0000951struct strpush {
952 struct strpush *prev; /* preceding string on stack */
953 char *prevstring;
954 int prevnleft;
955#if ENABLE_ASH_ALIAS
956 struct alias *ap; /* if push was associated with an alias */
957#endif
958 char *string; /* remember the string since it may change */
959};
960
961struct parsefile {
962 struct parsefile *prev; /* preceding file on stack */
963 int linno; /* current line */
964 int fd; /* file descriptor (or -1 if string) */
965 int nleft; /* number of chars left in this line */
966 int lleft; /* number of chars left in this buffer */
967 char *nextc; /* next char in buffer */
968 char *buf; /* input buffer */
969 struct strpush *strpush; /* for pushing strings at this level */
970 struct strpush basestrpush; /* so pushing one is fast */
971};
972
973static struct parsefile basepf; /* top level input file */
974static struct parsefile *parsefile = &basepf; /* current input file */
975static int startlinno; /* line # where last token started */
976static char *commandname; /* currently executing command */
977static struct strlist *cmdenviron; /* environment for builtin command */
978static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000979
980
981/* ============ Message printing */
982
983static void
984ash_vmsg(const char *msg, va_list ap)
985{
986 fprintf(stderr, "%s: ", arg0);
987 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +0000988 if (strcmp(arg0, commandname))
989 fprintf(stderr, "%s: ", commandname);
990 if (!iflag || parsefile->fd)
991 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +0000992 }
Denis Vlasenkob012b102007-02-19 22:43:01 +0000993 vfprintf(stderr, msg, ap);
994 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +0000995}
Denis Vlasenkob012b102007-02-19 22:43:01 +0000996
997/*
998 * Exverror is called to raise the error exception. If the second argument
999 * is not NULL then error prints an error message using printf style
1000 * formatting. It then raises the error exception.
1001 */
1002static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1003static void
1004ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001005{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001006#if DEBUG
1007 if (msg) {
1008 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1009 TRACEV((msg, ap));
1010 TRACE(("\") pid=%d\n", getpid()));
1011 } else
1012 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1013 if (msg)
1014#endif
1015 ash_vmsg(msg, ap);
1016
1017 flush_stdout_stderr();
1018 raise_exception(cond);
1019 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001020}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001021
1022static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1023static void
1024ash_msg_and_raise_error(const char *msg, ...)
1025{
1026 va_list ap;
1027
1028 va_start(ap, msg);
1029 ash_vmsg_and_raise(EXERROR, msg, ap);
1030 /* NOTREACHED */
1031 va_end(ap);
1032}
1033
1034static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1035static void
1036ash_msg_and_raise(int cond, const char *msg, ...)
1037{
1038 va_list ap;
1039
1040 va_start(ap, msg);
1041 ash_vmsg_and_raise(cond, msg, ap);
1042 /* NOTREACHED */
1043 va_end(ap);
1044}
1045
1046/*
1047 * error/warning routines for external builtins
1048 */
1049static void
1050ash_msg(const char *fmt, ...)
1051{
1052 va_list ap;
1053
1054 va_start(ap, fmt);
1055 ash_vmsg(fmt, ap);
1056 va_end(ap);
1057}
1058
1059/*
1060 * Return a string describing an error. The returned string may be a
1061 * pointer to a static buffer that will be overwritten on the next call.
1062 * Action describes the operation that got the error.
1063 */
1064static const char *
1065errmsg(int e, const char *em)
1066{
1067 if (e == ENOENT || e == ENOTDIR) {
1068 return em;
1069 }
1070 return strerror(e);
1071}
1072
1073
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001074/* ============ Memory allocation */
1075
1076/*
1077 * It appears that grabstackstr() will barf with such alignments
1078 * because stalloc() will return a string allocated in a new stackblock.
1079 */
1080#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1081enum {
1082 /* Most machines require the value returned from malloc to be aligned
1083 * in some way. The following macro will get this right
1084 * on many machines. */
1085 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1086 /* Minimum size of a block */
1087 MINSIZE = SHELL_ALIGN(504),
1088};
1089
1090struct stack_block {
1091 struct stack_block *prev;
1092 char space[MINSIZE];
1093};
1094
1095struct stackmark {
1096 struct stack_block *stackp;
1097 char *stacknxt;
1098 size_t stacknleft;
1099 struct stackmark *marknext;
1100};
1101
1102static struct stack_block stackbase;
1103static struct stack_block *stackp = &stackbase;
1104static struct stackmark *markp;
1105static char *stacknxt = stackbase.space;
1106static size_t stacknleft = MINSIZE;
1107static char *sstrend = stackbase.space + MINSIZE;
1108static int herefd = -1;
1109
1110#define stackblock() ((void *)stacknxt)
1111#define stackblocksize() stacknleft
1112
1113static void *
1114ckrealloc(void * p, size_t nbytes)
1115{
1116 p = realloc(p, nbytes);
1117 if (!p)
1118 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1119 return p;
1120}
1121
1122static void *
1123ckmalloc(size_t nbytes)
1124{
1125 return ckrealloc(NULL, nbytes);
1126}
1127
1128/*
1129 * Make a copy of a string in safe storage.
1130 */
1131static char *
1132ckstrdup(const char *s)
1133{
1134 char *p = strdup(s);
1135 if (!p)
1136 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1137 return p;
1138}
1139
1140/*
1141 * Parse trees for commands are allocated in lifo order, so we use a stack
1142 * to make this more efficient, and also to avoid all sorts of exception
1143 * handling code to handle interrupts in the middle of a parse.
1144 *
1145 * The size 504 was chosen because the Ultrix malloc handles that size
1146 * well.
1147 */
1148static void *
1149stalloc(size_t nbytes)
1150{
1151 char *p;
1152 size_t aligned;
1153
1154 aligned = SHELL_ALIGN(nbytes);
1155 if (aligned > stacknleft) {
1156 size_t len;
1157 size_t blocksize;
1158 struct stack_block *sp;
1159
1160 blocksize = aligned;
1161 if (blocksize < MINSIZE)
1162 blocksize = MINSIZE;
1163 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1164 if (len < blocksize)
1165 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1166 INT_OFF;
1167 sp = ckmalloc(len);
1168 sp->prev = stackp;
1169 stacknxt = sp->space;
1170 stacknleft = blocksize;
1171 sstrend = stacknxt + blocksize;
1172 stackp = sp;
1173 INT_ON;
1174 }
1175 p = stacknxt;
1176 stacknxt += aligned;
1177 stacknleft -= aligned;
1178 return p;
1179}
1180
1181static void
1182stunalloc(void *p)
1183{
1184#if DEBUG
1185 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
1186 write(2, "stunalloc\n", 10);
1187 abort();
1188 }
1189#endif
1190 stacknleft += stacknxt - (char *)p;
1191 stacknxt = p;
1192}
1193
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001194/*
1195 * Like strdup but works with the ash stack.
1196 */
1197static char *
1198ststrdup(const char *p)
1199{
1200 size_t len = strlen(p) + 1;
1201 return memcpy(stalloc(len), p, len);
1202}
1203
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001204static void
1205setstackmark(struct stackmark *mark)
1206{
1207 mark->stackp = stackp;
1208 mark->stacknxt = stacknxt;
1209 mark->stacknleft = stacknleft;
1210 mark->marknext = markp;
1211 markp = mark;
1212}
1213
1214static void
1215popstackmark(struct stackmark *mark)
1216{
1217 struct stack_block *sp;
1218
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001219 if (!mark->stackp)
1220 return;
1221
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001222 INT_OFF;
1223 markp = mark->marknext;
1224 while (stackp != mark->stackp) {
1225 sp = stackp;
1226 stackp = sp->prev;
1227 free(sp);
1228 }
1229 stacknxt = mark->stacknxt;
1230 stacknleft = mark->stacknleft;
1231 sstrend = mark->stacknxt + mark->stacknleft;
1232 INT_ON;
1233}
1234
1235/*
1236 * When the parser reads in a string, it wants to stick the string on the
1237 * stack and only adjust the stack pointer when it knows how big the
1238 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1239 * of space on top of the stack and stackblocklen returns the length of
1240 * this block. Growstackblock will grow this space by at least one byte,
1241 * possibly moving it (like realloc). Grabstackblock actually allocates the
1242 * part of the block that has been used.
1243 */
1244static void
1245growstackblock(void)
1246{
1247 size_t newlen;
1248
1249 newlen = stacknleft * 2;
1250 if (newlen < stacknleft)
1251 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1252 if (newlen < 128)
1253 newlen += 128;
1254
1255 if (stacknxt == stackp->space && stackp != &stackbase) {
1256 struct stack_block *oldstackp;
1257 struct stackmark *xmark;
1258 struct stack_block *sp;
1259 struct stack_block *prevstackp;
1260 size_t grosslen;
1261
1262 INT_OFF;
1263 oldstackp = stackp;
1264 sp = stackp;
1265 prevstackp = sp->prev;
1266 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1267 sp = ckrealloc(sp, grosslen);
1268 sp->prev = prevstackp;
1269 stackp = sp;
1270 stacknxt = sp->space;
1271 stacknleft = newlen;
1272 sstrend = sp->space + newlen;
1273
1274 /*
1275 * Stack marks pointing to the start of the old block
1276 * must be relocated to point to the new block
1277 */
1278 xmark = markp;
1279 while (xmark != NULL && xmark->stackp == oldstackp) {
1280 xmark->stackp = stackp;
1281 xmark->stacknxt = stacknxt;
1282 xmark->stacknleft = stacknleft;
1283 xmark = xmark->marknext;
1284 }
1285 INT_ON;
1286 } else {
1287 char *oldspace = stacknxt;
1288 int oldlen = stacknleft;
1289 char *p = stalloc(newlen);
1290
1291 /* free the space we just allocated */
1292 stacknxt = memcpy(p, oldspace, oldlen);
1293 stacknleft += newlen;
1294 }
1295}
1296
1297static void
1298grabstackblock(size_t len)
1299{
1300 len = SHELL_ALIGN(len);
1301 stacknxt += len;
1302 stacknleft -= len;
1303}
1304
1305/*
1306 * The following routines are somewhat easier to use than the above.
1307 * The user declares a variable of type STACKSTR, which may be declared
1308 * to be a register. The macro STARTSTACKSTR initializes things. Then
1309 * the user uses the macro STPUTC to add characters to the string. In
1310 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1311 * grown as necessary. When the user is done, she can just leave the
1312 * string there and refer to it using stackblock(). Or she can allocate
1313 * the space for it using grabstackstr(). If it is necessary to allow
1314 * someone else to use the stack temporarily and then continue to grow
1315 * the string, the user should use grabstack to allocate the space, and
1316 * then call ungrabstr(p) to return to the previous mode of operation.
1317 *
1318 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1319 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1320 * is space for at least one character.
1321 */
1322static void *
1323growstackstr(void)
1324{
1325 size_t len = stackblocksize();
1326 if (herefd >= 0 && len >= 1024) {
1327 full_write(herefd, stackblock(), len);
1328 return stackblock();
1329 }
1330 growstackblock();
1331 return stackblock() + len;
1332}
1333
1334/*
1335 * Called from CHECKSTRSPACE.
1336 */
1337static char *
1338makestrspace(size_t newlen, char *p)
1339{
1340 size_t len = p - stacknxt;
1341 size_t size = stackblocksize();
1342
1343 for (;;) {
1344 size_t nleft;
1345
1346 size = stackblocksize();
1347 nleft = size - len;
1348 if (nleft >= newlen)
1349 break;
1350 growstackblock();
1351 }
1352 return stackblock() + len;
1353}
1354
1355static char *
1356stack_nputstr(const char *s, size_t n, char *p)
1357{
1358 p = makestrspace(n, p);
1359 p = memcpy(p, s, n) + n;
1360 return p;
1361}
1362
1363static char *
1364stack_putstr(const char *s, char *p)
1365{
1366 return stack_nputstr(s, strlen(s), p);
1367}
1368
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001369static char *
1370_STPUTC(int c, char *p)
1371{
1372 if (p == sstrend)
1373 p = growstackstr();
1374 *p++ = c;
1375 return p;
1376}
1377
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001378#define STARTSTACKSTR(p) ((p) = stackblock())
1379#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001380#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001381 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001382 char *q = (p); \
1383 size_t l = (n); \
1384 size_t m = sstrend - q; \
1385 if (l > m) \
1386 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001387 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001388#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001389#define STACKSTRNUL(p) \
1390 do { \
1391 if ((p) == sstrend) \
1392 p = growstackstr(); \
1393 *p = '\0'; \
1394 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001395#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001396#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001397#define STADJUST(amount, p) (p += (amount))
1398
1399#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1400#define ungrabstackstr(s, p) stunalloc((s))
1401#define stackstrend() ((void *)sstrend)
1402
1403
1404/* ============ String helpers */
1405
1406/*
1407 * prefix -- see if pfx is a prefix of string.
1408 */
1409static char *
1410prefix(const char *string, const char *pfx)
1411{
1412 while (*pfx) {
1413 if (*pfx++ != *string++)
1414 return 0;
1415 }
1416 return (char *) string;
1417}
1418
1419/*
1420 * Check for a valid number. This should be elsewhere.
1421 */
1422static int
1423is_number(const char *p)
1424{
1425 do {
1426 if (!isdigit(*p))
1427 return 0;
1428 } while (*++p != '\0');
1429 return 1;
1430}
1431
1432/*
1433 * Convert a string of digits to an integer, printing an error message on
1434 * failure.
1435 */
1436static int
1437number(const char *s)
1438{
1439 if (!is_number(s))
1440 ash_msg_and_raise_error(illnum, s);
1441 return atoi(s);
1442}
1443
1444/*
1445 * Produce a possibly single quoted string suitable as input to the shell.
1446 * The return string is allocated on the stack.
1447 */
1448static char *
1449single_quote(const char *s)
1450{
1451 char *p;
1452
1453 STARTSTACKSTR(p);
1454
1455 do {
1456 char *q;
1457 size_t len;
1458
1459 len = strchrnul(s, '\'') - s;
1460
1461 q = p = makestrspace(len + 3, p);
1462
1463 *q++ = '\'';
1464 q = memcpy(q, s, len) + len;
1465 *q++ = '\'';
1466 s += len;
1467
1468 STADJUST(q - p, p);
1469
1470 len = strspn(s, "'");
1471 if (!len)
1472 break;
1473
1474 q = p = makestrspace(len + 3, p);
1475
1476 *q++ = '"';
1477 q = memcpy(q, s, len) + len;
1478 *q++ = '"';
1479 s += len;
1480
1481 STADJUST(q - p, p);
1482 } while (*s);
1483
1484 USTPUTC(0, p);
1485
1486 return stackblock();
1487}
1488
1489
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001490/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001491
1492static char **argptr; /* argument list for builtin commands */
1493static char *optionarg; /* set by nextopt (like getopt) */
1494static char *optptr; /* used by nextopt */
1495
1496/*
1497 * XXX - should get rid of. have all builtins use getopt(3). the
1498 * library getopt must have the BSD extension static variable "optreset"
1499 * otherwise it can't be used within the shell safely.
1500 *
1501 * Standard option processing (a la getopt) for builtin routines. The
1502 * only argument that is passed to nextopt is the option string; the
1503 * other arguments are unnecessary. It return the character, or '\0' on
1504 * end of input.
1505 */
1506static int
1507nextopt(const char *optstring)
1508{
1509 char *p;
1510 const char *q;
1511 char c;
1512
1513 p = optptr;
1514 if (p == NULL || *p == '\0') {
1515 p = *argptr;
1516 if (p == NULL || *p != '-' || *++p == '\0')
1517 return '\0';
1518 argptr++;
1519 if (LONE_DASH(p)) /* check for "--" */
1520 return '\0';
1521 }
1522 c = *p++;
1523 for (q = optstring; *q != c; ) {
1524 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001525 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001526 if (*++q == ':')
1527 q++;
1528 }
1529 if (*++q == ':') {
1530 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001531 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001532 optionarg = p;
1533 p = NULL;
1534 }
1535 optptr = p;
1536 return c;
1537}
1538
1539
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001540/* ============ Math support definitions */
1541
1542#if ENABLE_ASH_MATH_SUPPORT_64
1543typedef int64_t arith_t;
1544#define arith_t_type long long
1545#else
1546typedef long arith_t;
1547#define arith_t_type long
1548#endif
1549
1550#if ENABLE_ASH_MATH_SUPPORT
1551static arith_t dash_arith(const char *);
1552static arith_t arith(const char *expr, int *perrcode);
1553#endif
1554
1555#if ENABLE_ASH_RANDOM_SUPPORT
1556static unsigned long rseed;
1557#ifndef DYNAMIC_VAR
1558#define DYNAMIC_VAR
1559#endif
1560#endif
1561
1562
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001563/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001564
1565/* flags */
1566#define VEXPORT 0x01 /* variable is exported */
1567#define VREADONLY 0x02 /* variable cannot be modified */
1568#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1569#define VTEXTFIXED 0x08 /* text is statically allocated */
1570#define VSTACK 0x10 /* text is allocated on the stack */
1571#define VUNSET 0x20 /* the variable is not set */
1572#define VNOFUNC 0x40 /* don't call the callback function */
1573#define VNOSET 0x80 /* do not set variable - just readonly test */
1574#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1575#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001576# define VDYNAMIC 0x200 /* dynamic variable */
1577#else
1578# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001579#endif
1580
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001581static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1582#ifdef IFS_BROKEN
1583static const char defifsvar[] = "IFS= \t\n";
1584#define defifs (defifsvar + 4)
1585#else
1586static const char defifs[] = " \t\n";
1587#endif
1588
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001589struct shparam {
1590 int nparam; /* # of positional parameters (without $0) */
1591 unsigned char malloc; /* if parameter list dynamically allocated */
1592 char **p; /* parameter list */
1593#if ENABLE_ASH_GETOPTS
1594 int optind; /* next parameter to be processed by getopts */
1595 int optoff; /* used by getopts */
1596#endif
1597};
1598
1599static struct shparam shellparam; /* $@ current positional parameters */
1600
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00001601/*
1602 * Free the list of positional parameters.
1603 */
1604static void
1605freeparam(volatile struct shparam *param)
1606{
1607 char **ap;
1608
1609 if (param->malloc) {
1610 for (ap = param->p; *ap; ap++)
1611 free(*ap);
1612 free(param->p);
1613 }
1614}
1615
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001616#if ENABLE_ASH_GETOPTS
1617static void
1618getoptsreset(const char *value)
1619{
1620 shellparam.optind = number(value);
1621 shellparam.optoff = -1;
1622}
1623#endif
1624
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001625struct var {
1626 struct var *next; /* next entry in hash list */
1627 int flags; /* flags are defined above */
1628 const char *text; /* name=value */
1629 void (*func)(const char *); /* function to be called when */
1630 /* the variable gets set/unset */
1631};
1632
1633struct localvar {
1634 struct localvar *next; /* next local variable in list */
1635 struct var *vp; /* the variable that was made local */
1636 int flags; /* saved flags */
1637 const char *text; /* saved text */
1638};
1639
1640/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001641#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001642static void
1643change_lc_all(const char *value)
1644{
1645 if (value && *value != '\0')
1646 setlocale(LC_ALL, value);
1647}
1648static void
1649change_lc_ctype(const char *value)
1650{
1651 if (value && *value != '\0')
1652 setlocale(LC_CTYPE, value);
1653}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001654#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001655#if ENABLE_ASH_MAIL
1656static void chkmail(void);
1657static void changemail(const char *);
1658#endif
1659static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001660#if ENABLE_ASH_RANDOM_SUPPORT
1661static void change_random(const char *);
1662#endif
1663
1664static struct var varinit[] = {
1665#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001666 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001667#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001668 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001669#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001670#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001671 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1672 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001673#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001674 { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1675 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1676 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1677 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001678#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001679 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001680#endif
1681#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001682 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001683#endif
1684#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001685 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1686 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001687#endif
1688#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001689 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001690#endif
1691};
1692
1693#define vifs varinit[0]
1694#if ENABLE_ASH_MAIL
1695#define vmail (&vifs)[1]
1696#define vmpath (&vmail)[1]
1697#else
1698#define vmpath vifs
1699#endif
1700#define vpath (&vmpath)[1]
1701#define vps1 (&vpath)[1]
1702#define vps2 (&vps1)[1]
1703#define vps4 (&vps2)[1]
1704#define voptind (&vps4)[1]
1705#if ENABLE_ASH_GETOPTS
1706#define vrandom (&voptind)[1]
1707#else
1708#define vrandom (&vps4)[1]
1709#endif
1710#define defpath (defpathvar + 5)
1711
1712/*
1713 * The following macros access the values of the above variables.
1714 * They have to skip over the name. They return the null string
1715 * for unset variables.
1716 */
1717#define ifsval() (vifs.text + 4)
1718#define ifsset() ((vifs.flags & VUNSET) == 0)
1719#define mailval() (vmail.text + 5)
1720#define mpathval() (vmpath.text + 9)
1721#define pathval() (vpath.text + 5)
1722#define ps1val() (vps1.text + 4)
1723#define ps2val() (vps2.text + 4)
1724#define ps4val() (vps4.text + 4)
1725#define optindval() (voptind.text + 7)
1726
1727#define mpathset() ((vmpath.flags & VUNSET) == 0)
1728
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729/*
1730 * The parsefile structure pointed to by the global variable parsefile
1731 * contains information about the current file being read.
1732 */
1733struct redirtab {
1734 struct redirtab *next;
1735 int renamed[10];
1736 int nullredirs;
1737};
1738
1739static struct redirtab *redirlist;
1740static int nullredirs;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001741extern char **environ;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001742static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1743
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#define VTABSIZE 39
1745
1746static struct var *vartab[VTABSIZE];
1747
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1749#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1750
1751/*
1752 * Return of a legal variable name (a letter or underscore followed by zero or
1753 * more letters, underscores, and digits).
1754 */
1755static char *
1756endofname(const char *name)
1757{
1758 char *p;
1759
1760 p = (char *) name;
1761 if (!is_name(*p))
1762 return p;
1763 while (*++p) {
1764 if (!is_in_name(*p))
1765 break;
1766 }
1767 return p;
1768}
1769
1770/*
1771 * Compares two strings up to the first = or '\0'. The first
1772 * string must be terminated by '='; the second may be terminated by
1773 * either '=' or '\0'.
1774 */
1775static int
1776varcmp(const char *p, const char *q)
1777{
1778 int c, d;
1779
1780 while ((c = *p) == (d = *q)) {
1781 if (!c || c == '=')
1782 goto out;
1783 p++;
1784 q++;
1785 }
1786 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001787 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001788 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001789 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790 out:
1791 return c - d;
1792}
1793
1794static int
1795varequal(const char *a, const char *b)
1796{
1797 return !varcmp(a, b);
1798}
1799
1800/*
1801 * Find the appropriate entry in the hash table from the name.
1802 */
1803static struct var **
1804hashvar(const char *p)
1805{
1806 unsigned hashval;
1807
1808 hashval = ((unsigned char) *p) << 4;
1809 while (*p && *p != '=')
1810 hashval += (unsigned char) *p++;
1811 return &vartab[hashval % VTABSIZE];
1812}
1813
1814static int
1815vpcmp(const void *a, const void *b)
1816{
1817 return varcmp(*(const char **)a, *(const char **)b);
1818}
1819
1820/*
1821 * This routine initializes the builtin variables.
1822 */
1823static void
1824initvar(void)
1825{
1826 struct var *vp;
1827 struct var *end;
1828 struct var **vpp;
1829
1830 /*
1831 * PS1 depends on uid
1832 */
1833#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1834 vps1.text = "PS1=\\w \\$ ";
1835#else
1836 if (!geteuid())
1837 vps1.text = "PS1=# ";
1838#endif
1839 vp = varinit;
1840 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1841 do {
1842 vpp = hashvar(vp->text);
1843 vp->next = *vpp;
1844 *vpp = vp;
1845 } while (++vp < end);
1846}
1847
1848static struct var **
1849findvar(struct var **vpp, const char *name)
1850{
1851 for (; *vpp; vpp = &(*vpp)->next) {
1852 if (varequal((*vpp)->text, name)) {
1853 break;
1854 }
1855 }
1856 return vpp;
1857}
1858
1859/*
1860 * Find the value of a variable. Returns NULL if not set.
1861 */
1862static char *
1863lookupvar(const char *name)
1864{
1865 struct var *v;
1866
1867 v = *findvar(hashvar(name), name);
1868 if (v) {
1869#ifdef DYNAMIC_VAR
1870 /*
1871 * Dynamic variables are implemented roughly the same way they are
1872 * in bash. Namely, they're "special" so long as they aren't unset.
1873 * As soon as they're unset, they're no longer dynamic, and dynamic
1874 * lookup will no longer happen at that point. -- PFM.
1875 */
1876 if ((v->flags & VDYNAMIC))
1877 (*v->func)(NULL);
1878#endif
1879 if (!(v->flags & VUNSET))
1880 return strchrnul(v->text, '=') + 1;
1881 }
1882 return NULL;
1883}
1884
1885/*
1886 * Search the environment of a builtin command.
1887 */
1888static char *
1889bltinlookup(const char *name)
1890{
1891 struct strlist *sp;
1892
1893 for (sp = cmdenviron; sp; sp = sp->next) {
1894 if (varequal(sp->text, name))
1895 return strchrnul(sp->text, '=') + 1;
1896 }
1897 return lookupvar(name);
1898}
1899
1900/*
1901 * Same as setvar except that the variable and value are passed in
1902 * the first argument as name=value. Since the first argument will
1903 * be actually stored in the table, it should not be a string that
1904 * will go away.
1905 * Called with interrupts off.
1906 */
1907static void
1908setvareq(char *s, int flags)
1909{
1910 struct var *vp, **vpp;
1911
1912 vpp = hashvar(s);
1913 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1914 vp = *findvar(vpp, s);
1915 if (vp) {
1916 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1917 const char *n;
1918
1919 if (flags & VNOSAVE)
1920 free(s);
1921 n = vp->text;
1922 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1923 }
1924
1925 if (flags & VNOSET)
1926 return;
1927
1928 if (vp->func && (flags & VNOFUNC) == 0)
1929 (*vp->func)(strchrnul(s, '=') + 1);
1930
1931 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1932 free((char*)vp->text);
1933
1934 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1935 } else {
1936 if (flags & VNOSET)
1937 return;
1938 /* not found */
1939 vp = ckmalloc(sizeof(*vp));
1940 vp->next = *vpp;
1941 vp->func = NULL;
1942 *vpp = vp;
1943 }
1944 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1945 s = ckstrdup(s);
1946 vp->text = s;
1947 vp->flags = flags;
1948}
1949
1950/*
1951 * Set the value of a variable. The flags argument is ored with the
1952 * flags of the variable. If val is NULL, the variable is unset.
1953 */
1954static void
1955setvar(const char *name, const char *val, int flags)
1956{
1957 char *p, *q;
1958 size_t namelen;
1959 char *nameeq;
1960 size_t vallen;
1961
1962 q = endofname(name);
1963 p = strchrnul(q, '=');
1964 namelen = p - name;
1965 if (!namelen || p != q)
1966 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1967 vallen = 0;
1968 if (val == NULL) {
1969 flags |= VUNSET;
1970 } else {
1971 vallen = strlen(val);
1972 }
1973 INT_OFF;
1974 nameeq = ckmalloc(namelen + vallen + 2);
1975 p = memcpy(nameeq, name, namelen) + namelen;
1976 if (val) {
1977 *p++ = '=';
1978 p = memcpy(p, val, vallen) + vallen;
1979 }
1980 *p = '\0';
1981 setvareq(nameeq, flags | VNOSAVE);
1982 INT_ON;
1983}
1984
1985#if ENABLE_ASH_GETOPTS
1986/*
1987 * Safe version of setvar, returns 1 on success 0 on failure.
1988 */
1989static int
1990setvarsafe(const char *name, const char *val, int flags)
1991{
1992 int err;
1993 volatile int saveint;
1994 struct jmploc *volatile savehandler = exception_handler;
1995 struct jmploc jmploc;
1996
1997 SAVE_INT(saveint);
1998 if (setjmp(jmploc.loc))
1999 err = 1;
2000 else {
2001 exception_handler = &jmploc;
2002 setvar(name, val, flags);
2003 err = 0;
2004 }
2005 exception_handler = savehandler;
2006 RESTORE_INT(saveint);
2007 return err;
2008}
2009#endif
2010
2011/*
2012 * Unset the specified variable.
2013 */
2014static int
2015unsetvar(const char *s)
2016{
2017 struct var **vpp;
2018 struct var *vp;
2019 int retval;
2020
2021 vpp = findvar(hashvar(s), s);
2022 vp = *vpp;
2023 retval = 2;
2024 if (vp) {
2025 int flags = vp->flags;
2026
2027 retval = 1;
2028 if (flags & VREADONLY)
2029 goto out;
2030#ifdef DYNAMIC_VAR
2031 vp->flags &= ~VDYNAMIC;
2032#endif
2033 if (flags & VUNSET)
2034 goto ok;
2035 if ((flags & VSTRFIXED) == 0) {
2036 INT_OFF;
2037 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2038 free((char*)vp->text);
2039 *vpp = vp->next;
2040 free(vp);
2041 INT_ON;
2042 } else {
2043 setvar(s, 0, 0);
2044 vp->flags &= ~VEXPORT;
2045 }
2046 ok:
2047 retval = 0;
2048 }
2049 out:
2050 return retval;
2051}
2052
2053/*
2054 * Process a linked list of variable assignments.
2055 */
2056static void
2057listsetvar(struct strlist *list_set_var, int flags)
2058{
2059 struct strlist *lp = list_set_var;
2060
2061 if (!lp)
2062 return;
2063 INT_OFF;
2064 do {
2065 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002066 lp = lp->next;
2067 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002068 INT_ON;
2069}
2070
2071/*
2072 * Generate a list of variables satisfying the given conditions.
2073 */
2074static char **
2075listvars(int on, int off, char ***end)
2076{
2077 struct var **vpp;
2078 struct var *vp;
2079 char **ep;
2080 int mask;
2081
2082 STARTSTACKSTR(ep);
2083 vpp = vartab;
2084 mask = on | off;
2085 do {
2086 for (vp = *vpp; vp; vp = vp->next) {
2087 if ((vp->flags & mask) == on) {
2088 if (ep == stackstrend())
2089 ep = growstackstr();
2090 *ep++ = (char *) vp->text;
2091 }
2092 }
2093 } while (++vpp < vartab + VTABSIZE);
2094 if (ep == stackstrend())
2095 ep = growstackstr();
2096 if (end)
2097 *end = ep;
2098 *ep++ = NULL;
2099 return grabstackstr(ep);
2100}
2101
2102
2103/* ============ Path search helper
2104 *
2105 * The variable path (passed by reference) should be set to the start
2106 * of the path before the first call; padvance will update
2107 * this value as it proceeds. Successive calls to padvance will return
2108 * the possible path expansions in sequence. If an option (indicated by
2109 * a percent sign) appears in the path entry then the global variable
2110 * pathopt will be set to point to it; otherwise pathopt will be set to
2111 * NULL.
2112 */
2113static const char *pathopt; /* set by padvance */
2114
2115static char *
2116padvance(const char **path, const char *name)
2117{
2118 const char *p;
2119 char *q;
2120 const char *start;
2121 size_t len;
2122
2123 if (*path == NULL)
2124 return NULL;
2125 start = *path;
2126 for (p = start; *p && *p != ':' && *p != '%'; p++);
2127 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2128 while (stackblocksize() < len)
2129 growstackblock();
2130 q = stackblock();
2131 if (p != start) {
2132 memcpy(q, start, p - start);
2133 q += p - start;
2134 *q++ = '/';
2135 }
2136 strcpy(q, name);
2137 pathopt = NULL;
2138 if (*p == '%') {
2139 pathopt = ++p;
2140 while (*p && *p != ':') p++;
2141 }
2142 if (*p == ':')
2143 *path = p + 1;
2144 else
2145 *path = NULL;
2146 return stalloc(len);
2147}
2148
2149
2150/* ============ Prompt */
2151
2152static int doprompt; /* if set, prompt the user */
2153static int needprompt; /* true if interactive and at start of line */
2154
2155#if ENABLE_FEATURE_EDITING
2156static line_input_t *line_input_state;
2157static const char *cmdedit_prompt;
2158static void
2159putprompt(const char *s)
2160{
2161 if (ENABLE_ASH_EXPAND_PRMT) {
2162 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002163 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002164 return;
2165 }
2166 cmdedit_prompt = s;
2167}
2168#else
2169static void
2170putprompt(const char *s)
2171{
2172 out2str(s);
2173}
2174#endif
2175
2176#if ENABLE_ASH_EXPAND_PRMT
2177/* expandstr() needs parsing machinery, so it is far away ahead... */
2178static const char *expandstr(const char *ps);
2179#else
2180#define expandstr(s) s
2181#endif
2182
2183static void
2184setprompt(int whichprompt)
2185{
2186 const char *prompt;
2187#if ENABLE_ASH_EXPAND_PRMT
2188 struct stackmark smark;
2189#endif
2190
2191 needprompt = 0;
2192
2193 switch (whichprompt) {
2194 case 1:
2195 prompt = ps1val();
2196 break;
2197 case 2:
2198 prompt = ps2val();
2199 break;
2200 default: /* 0 */
2201 prompt = nullstr;
2202 }
2203#if ENABLE_ASH_EXPAND_PRMT
2204 setstackmark(&smark);
2205 stalloc(stackblocksize());
2206#endif
2207 putprompt(expandstr(prompt));
2208#if ENABLE_ASH_EXPAND_PRMT
2209 popstackmark(&smark);
2210#endif
2211}
2212
2213
2214/* ============ The cd and pwd commands */
2215
2216#define CD_PHYSICAL 1
2217#define CD_PRINT 2
2218
2219static int docd(const char *, int);
2220
2221static char *curdir = nullstr; /* current working directory */
2222static char *physdir = nullstr; /* physical working directory */
2223
2224static int
2225cdopt(void)
2226{
2227 int flags = 0;
2228 int i, j;
2229
2230 j = 'L';
2231 while ((i = nextopt("LP"))) {
2232 if (i != j) {
2233 flags ^= CD_PHYSICAL;
2234 j = i;
2235 }
2236 }
2237
2238 return flags;
2239}
2240
2241/*
2242 * Update curdir (the name of the current directory) in response to a
2243 * cd command.
2244 */
2245static const char *
2246updatepwd(const char *dir)
2247{
2248 char *new;
2249 char *p;
2250 char *cdcomppath;
2251 const char *lim;
2252
2253 cdcomppath = ststrdup(dir);
2254 STARTSTACKSTR(new);
2255 if (*dir != '/') {
2256 if (curdir == nullstr)
2257 return 0;
2258 new = stack_putstr(curdir, new);
2259 }
2260 new = makestrspace(strlen(dir) + 2, new);
2261 lim = stackblock() + 1;
2262 if (*dir != '/') {
2263 if (new[-1] != '/')
2264 USTPUTC('/', new);
2265 if (new > lim && *lim == '/')
2266 lim++;
2267 } else {
2268 USTPUTC('/', new);
2269 cdcomppath++;
2270 if (dir[1] == '/' && dir[2] != '/') {
2271 USTPUTC('/', new);
2272 cdcomppath++;
2273 lim++;
2274 }
2275 }
2276 p = strtok(cdcomppath, "/");
2277 while (p) {
2278 switch (*p) {
2279 case '.':
2280 if (p[1] == '.' && p[2] == '\0') {
2281 while (new > lim) {
2282 STUNPUTC(new);
2283 if (new[-1] == '/')
2284 break;
2285 }
2286 break;
2287 } else if (p[1] == '\0')
2288 break;
2289 /* fall through */
2290 default:
2291 new = stack_putstr(p, new);
2292 USTPUTC('/', new);
2293 }
2294 p = strtok(0, "/");
2295 }
2296 if (new > lim)
2297 STUNPUTC(new);
2298 *new = 0;
2299 return stackblock();
2300}
2301
2302/*
2303 * Find out what the current directory is. If we already know the current
2304 * directory, this routine returns immediately.
2305 */
2306static char *
2307getpwd(void)
2308{
2309 char *dir = getcwd(0, 0);
2310 return dir ? dir : nullstr;
2311}
2312
2313static void
2314setpwd(const char *val, int setold)
2315{
2316 char *oldcur, *dir;
2317
2318 oldcur = dir = curdir;
2319
2320 if (setold) {
2321 setvar("OLDPWD", oldcur, VEXPORT);
2322 }
2323 INT_OFF;
2324 if (physdir != nullstr) {
2325 if (physdir != oldcur)
2326 free(physdir);
2327 physdir = nullstr;
2328 }
2329 if (oldcur == val || !val) {
2330 char *s = getpwd();
2331 physdir = s;
2332 if (!val)
2333 dir = s;
2334 } else
2335 dir = ckstrdup(val);
2336 if (oldcur != dir && oldcur != nullstr) {
2337 free(oldcur);
2338 }
2339 curdir = dir;
2340 INT_ON;
2341 setvar("PWD", dir, VEXPORT);
2342}
2343
2344static void hashcd(void);
2345
2346/*
2347 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2348 * know that the current directory has changed.
2349 */
2350static int
2351docd(const char *dest, int flags)
2352{
2353 const char *dir = 0;
2354 int err;
2355
2356 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2357
2358 INT_OFF;
2359 if (!(flags & CD_PHYSICAL)) {
2360 dir = updatepwd(dest);
2361 if (dir)
2362 dest = dir;
2363 }
2364 err = chdir(dest);
2365 if (err)
2366 goto out;
2367 setpwd(dir, 1);
2368 hashcd();
2369 out:
2370 INT_ON;
2371 return err;
2372}
2373
2374static int
2375cdcmd(int argc, char **argv)
2376{
2377 const char *dest;
2378 const char *path;
2379 const char *p;
2380 char c;
2381 struct stat statb;
2382 int flags;
2383
2384 flags = cdopt();
2385 dest = *argptr;
2386 if (!dest)
2387 dest = bltinlookup(homestr);
2388 else if (LONE_DASH(dest)) {
2389 dest = bltinlookup("OLDPWD");
2390 flags |= CD_PRINT;
2391 }
2392 if (!dest)
2393 dest = nullstr;
2394 if (*dest == '/')
2395 goto step7;
2396 if (*dest == '.') {
2397 c = dest[1];
2398 dotdot:
2399 switch (c) {
2400 case '\0':
2401 case '/':
2402 goto step6;
2403 case '.':
2404 c = dest[2];
2405 if (c != '.')
2406 goto dotdot;
2407 }
2408 }
2409 if (!*dest)
2410 dest = ".";
2411 path = bltinlookup("CDPATH");
2412 if (!path) {
2413 step6:
2414 step7:
2415 p = dest;
2416 goto docd;
2417 }
2418 do {
2419 c = *path;
2420 p = padvance(&path, dest);
2421 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2422 if (c && c != ':')
2423 flags |= CD_PRINT;
2424 docd:
2425 if (!docd(p, flags))
2426 goto out;
2427 break;
2428 }
2429 } while (path);
2430 ash_msg_and_raise_error("can't cd to %s", dest);
2431 /* NOTREACHED */
2432 out:
2433 if (flags & CD_PRINT)
2434 out1fmt(snlfmt, curdir);
2435 return 0;
2436}
2437
2438static int
2439pwdcmd(int argc, char **argv)
2440{
2441 int flags;
2442 const char *dir = curdir;
2443
2444 flags = cdopt();
2445 if (flags) {
2446 if (physdir == nullstr)
2447 setpwd(dir, 0);
2448 dir = physdir;
2449 }
2450 out1fmt(snlfmt, dir);
2451 return 0;
2452}
2453
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002454
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002455/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002456
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002457#define IBUFSIZ (BUFSIZ + 1)
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002458#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002459
Eric Andersenc470f442003-07-28 09:56:35 +00002460/* Syntax classes */
2461#define CWORD 0 /* character is nothing special */
2462#define CNL 1 /* newline character */
2463#define CBACK 2 /* a backslash character */
2464#define CSQUOTE 3 /* single quote */
2465#define CDQUOTE 4 /* double quote */
2466#define CENDQUOTE 5 /* a terminating quote */
2467#define CBQUOTE 6 /* backwards single quote */
2468#define CVAR 7 /* a dollar sign */
2469#define CENDVAR 8 /* a '}' character */
2470#define CLP 9 /* a left paren in arithmetic */
2471#define CRP 10 /* a right paren in arithmetic */
2472#define CENDFILE 11 /* end of file */
2473#define CCTL 12 /* like CWORD, except it must be escaped */
2474#define CSPCL 13 /* these terminate a word */
2475#define CIGN 14 /* character should be ignored */
2476
Denis Vlasenko131ae172007-02-18 13:00:19 +00002477#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002478#define SYNBASE 130
2479#define PEOF -130
2480#define PEOA -129
2481#define PEOA_OR_PEOF PEOA
2482#else
2483#define SYNBASE 129
2484#define PEOF -129
2485#define PEOA_OR_PEOF PEOF
2486#endif
2487
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002488/* number syntax index */
2489#define BASESYNTAX 0 /* not in quotes */
2490#define DQSYNTAX 1 /* in double quotes */
2491#define SQSYNTAX 2 /* in single quotes */
2492#define ARISYNTAX 3 /* in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002493
Denis Vlasenko131ae172007-02-18 13:00:19 +00002494#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002495#define USE_SIT_FUNCTION
2496#endif
2497
Denis Vlasenko131ae172007-02-18 13:00:19 +00002498#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002499static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002500#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002501 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002502#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002503 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2504 { CNL, CNL, CNL, CNL }, /* 2, \n */
2505 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2506 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2507 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2508 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2509 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2510 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2511 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2512 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2513 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002514#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002515 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2516 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2517 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002518#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002519};
Eric Andersenc470f442003-07-28 09:56:35 +00002520#else
2521static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002522#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002523 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002524#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002525 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2526 { CNL, CNL, CNL }, /* 2, \n */
2527 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2528 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2529 { CVAR, CVAR, CWORD }, /* 5, $ */
2530 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2531 { CSPCL, CWORD, CWORD }, /* 7, ( */
2532 { CSPCL, CWORD, CWORD }, /* 8, ) */
2533 { CBACK, CBACK, CCTL }, /* 9, \ */
2534 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2535 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002536#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002537 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2538 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2539 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002540#endif
2541};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002542#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002543
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002544#ifdef USE_SIT_FUNCTION
2545
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002546static int
2547SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002548{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002549 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002550#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002551 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002552 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2553 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2554 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2555 11, 3 /* "}~" */
2556 };
2557#else
2558 static const char syntax_index_table[] = {
2559 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2560 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2561 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2562 10, 2 /* "}~" */
2563 };
2564#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002565 const char *s;
2566 int indx;
2567
Eric Andersenc470f442003-07-28 09:56:35 +00002568 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002569 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002570#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002571 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002572 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002573 else
2574#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002575#define U_C(c) ((unsigned char)(c))
2576
2577 if ((unsigned char)c >= (unsigned char)(CTLESC)
2578 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2579 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002580 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002581 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002582 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002583 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002584 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002585 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002586 }
2587 return S_I_T[indx][syntax];
2588}
2589
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002590#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002591
Denis Vlasenko131ae172007-02-18 13:00:19 +00002592#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002593#define CSPCL_CIGN_CIGN_CIGN 0
2594#define CSPCL_CWORD_CWORD_CWORD 1
2595#define CNL_CNL_CNL_CNL 2
2596#define CWORD_CCTL_CCTL_CWORD 3
2597#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2598#define CVAR_CVAR_CWORD_CVAR 5
2599#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2600#define CSPCL_CWORD_CWORD_CLP 7
2601#define CSPCL_CWORD_CWORD_CRP 8
2602#define CBACK_CBACK_CCTL_CBACK 9
2603#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2604#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2605#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2606#define CWORD_CWORD_CWORD_CWORD 13
2607#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002608#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002609#define CSPCL_CWORD_CWORD_CWORD 0
2610#define CNL_CNL_CNL_CNL 1
2611#define CWORD_CCTL_CCTL_CWORD 2
2612#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2613#define CVAR_CVAR_CWORD_CVAR 4
2614#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2615#define CSPCL_CWORD_CWORD_CLP 6
2616#define CSPCL_CWORD_CWORD_CRP 7
2617#define CBACK_CBACK_CCTL_CBACK 8
2618#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2619#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2620#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2621#define CWORD_CWORD_CWORD_CWORD 12
2622#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002623#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002624
2625static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002626 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002627 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002628#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002629 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2630#endif
2631 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2632 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2633 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2634 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2635 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2636 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2637 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2638 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2639 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002640 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2641 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2642 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2643 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2644 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2645 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2646 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2647 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2648 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2649 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2650 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2769 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2770 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2792 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002793 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002794 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2795 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2796 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2797 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002798 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002799 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2800 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2801 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2802 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2803 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2804 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2805 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2806 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2807 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2808 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2809 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2810 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2811 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2812 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2813 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2814 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2815 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2816 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2817 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2818 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2819 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2820 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2821 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2822 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2823 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2824 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2825 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2826 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2827 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2828 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2829 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2830 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2831 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2832 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2833 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2851 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2852 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2853 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2854 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2855 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2856 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2857 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2858 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2859 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2860 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2861 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2862 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2863 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2865 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2866 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2884 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2885 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2886 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002887};
2888
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002889#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2890
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002891#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002892
Eric Andersen2870d962001-07-02 17:27:21 +00002893
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002894/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002895
Denis Vlasenko131ae172007-02-18 13:00:19 +00002896#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002897
2898#define ALIASINUSE 1
2899#define ALIASDEAD 2
2900
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002901#define ATABSIZE 39
2902
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002903struct alias {
2904 struct alias *next;
2905 char *name;
2906 char *val;
2907 int flag;
2908};
2909
Eric Andersen2870d962001-07-02 17:27:21 +00002910static struct alias *atab[ATABSIZE];
2911
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002912static struct alias **
2913__lookupalias(const char *name) {
2914 unsigned int hashval;
2915 struct alias **app;
2916 const char *p;
2917 unsigned int ch;
2918
2919 p = name;
2920
2921 ch = (unsigned char)*p;
2922 hashval = ch << 4;
2923 while (ch) {
2924 hashval += ch;
2925 ch = (unsigned char)*++p;
2926 }
2927 app = &atab[hashval % ATABSIZE];
2928
2929 for (; *app; app = &(*app)->next) {
2930 if (strcmp(name, (*app)->name) == 0) {
2931 break;
2932 }
2933 }
2934
2935 return app;
2936}
2937
2938static struct alias *
2939lookupalias(const char *name, int check)
2940{
2941 struct alias *ap = *__lookupalias(name);
2942
2943 if (check && ap && (ap->flag & ALIASINUSE))
2944 return NULL;
2945 return ap;
2946}
2947
2948static struct alias *
2949freealias(struct alias *ap)
2950{
2951 struct alias *next;
2952
2953 if (ap->flag & ALIASINUSE) {
2954 ap->flag |= ALIASDEAD;
2955 return ap;
2956 }
2957
2958 next = ap->next;
2959 free(ap->name);
2960 free(ap->val);
2961 free(ap);
2962 return next;
2963}
Eric Andersencb57d552001-06-28 07:25:16 +00002964
Eric Andersenc470f442003-07-28 09:56:35 +00002965static void
2966setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002967{
2968 struct alias *ap, **app;
2969
2970 app = __lookupalias(name);
2971 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002972 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002973 if (ap) {
2974 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002975 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002976 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002977 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002978 ap->flag &= ~ALIASDEAD;
2979 } else {
2980 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002981 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002982 ap->name = ckstrdup(name);
2983 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002984 ap->flag = 0;
2985 ap->next = 0;
2986 *app = ap;
2987 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002988 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002989}
2990
Eric Andersenc470f442003-07-28 09:56:35 +00002991static int
2992unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00002993{
Eric Andersencb57d552001-06-28 07:25:16 +00002994 struct alias **app;
2995
2996 app = __lookupalias(name);
2997
2998 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002999 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003000 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003001 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003002 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003003 }
3004
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003005 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003006}
3007
Eric Andersenc470f442003-07-28 09:56:35 +00003008static void
3009rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003010{
Eric Andersencb57d552001-06-28 07:25:16 +00003011 struct alias *ap, **app;
3012 int i;
3013
Denis Vlasenkob012b102007-02-19 22:43:01 +00003014 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003015 for (i = 0; i < ATABSIZE; i++) {
3016 app = &atab[i];
3017 for (ap = *app; ap; ap = *app) {
3018 *app = freealias(*app);
3019 if (ap == *app) {
3020 app = &ap->next;
3021 }
3022 }
3023 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003024 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003025}
3026
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003027static void
3028printalias(const struct alias *ap)
3029{
3030 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3031}
3032
Eric Andersencb57d552001-06-28 07:25:16 +00003033/*
3034 * TODO - sort output
3035 */
Eric Andersenc470f442003-07-28 09:56:35 +00003036static int
3037aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003038{
3039 char *n, *v;
3040 int ret = 0;
3041 struct alias *ap;
3042
3043 if (argc == 1) {
3044 int i;
3045
3046 for (i = 0; i < ATABSIZE; i++)
3047 for (ap = atab[i]; ap; ap = ap->next) {
3048 printalias(ap);
3049 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003050 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003051 }
3052 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003053 v = strchr(n+1, '=');
3054 if (v == NULL) { /* n+1: funny ksh stuff */
3055 ap = *__lookupalias(n);
3056 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003057 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003058 ret = 1;
3059 } else
3060 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003061 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003062 *v++ = '\0';
3063 setalias(n, v);
3064 }
3065 }
3066
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003067 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003068}
3069
Eric Andersenc470f442003-07-28 09:56:35 +00003070static int
3071unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003072{
3073 int i;
3074
3075 while ((i = nextopt("a")) != '\0') {
3076 if (i == 'a') {
3077 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003078 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003079 }
3080 }
3081 for (i = 0; *argptr; argptr++) {
3082 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003083 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003084 i = 1;
3085 }
3086 }
3087
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003088 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003089}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003090
Denis Vlasenko131ae172007-02-18 13:00:19 +00003091#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003092
Eric Andersenc470f442003-07-28 09:56:35 +00003093
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003094/* ============ jobs.c */
3095
3096/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3097#define FORK_FG 0
3098#define FORK_BG 1
3099#define FORK_NOJOB 2
3100
3101/* mode flags for showjob(s) */
3102#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3103#define SHOW_PID 0x04 /* include process pid */
3104#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3105
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003106/*
3107 * A job structure contains information about a job. A job is either a
3108 * single process or a set of processes contained in a pipeline. In the
3109 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3110 * array of pids.
3111 */
3112
3113struct procstat {
3114 pid_t pid; /* process id */
3115 int status; /* last process status from wait() */
3116 char *cmd; /* text of command being run */
3117};
3118
3119struct job {
3120 struct procstat ps0; /* status of process */
3121 struct procstat *ps; /* status or processes when more than one */
3122#if JOBS
3123 int stopstatus; /* status of a stopped job */
3124#endif
3125 uint32_t
3126 nprocs: 16, /* number of processes */
3127 state: 8,
3128#define JOBRUNNING 0 /* at least one proc running */
3129#define JOBSTOPPED 1 /* all procs are stopped */
3130#define JOBDONE 2 /* all procs are completed */
3131#if JOBS
3132 sigint: 1, /* job was killed by SIGINT */
3133 jobctl: 1, /* job running under job control */
3134#endif
3135 waited: 1, /* true if this entry has been waited for */
3136 used: 1, /* true if this entry is in used */
3137 changed: 1; /* true if status has changed */
3138 struct job *prev_job; /* previous job */
3139};
3140
3141static pid_t backgndpid; /* pid of last background process */
3142static int job_warning; /* user was warned about stopped jobs */
3143#if JOBS
3144static int jobctl; /* true if doing job control */
3145#endif
3146
3147static struct job *makejob(union node *, int);
3148static int forkshell(struct job *, union node *, int);
3149static int waitforjob(struct job *);
3150
3151#if ! JOBS
3152#define setjobctl(on) /* do nothing */
3153#else
3154static void setjobctl(int);
3155static void showjobs(FILE *, int);
3156#endif
3157
3158/*
3159 * Set the signal handler for the specified signal. The routine figures
3160 * out what it should be set to.
3161 */
3162static void
3163setsignal(int signo)
3164{
3165 int action;
3166 char *t, tsig;
3167 struct sigaction act;
3168
3169 t = trap[signo];
3170 if (t == NULL)
3171 action = S_DFL;
3172 else if (*t != '\0')
3173 action = S_CATCH;
3174 else
3175 action = S_IGN;
3176 if (rootshell && action == S_DFL) {
3177 switch (signo) {
3178 case SIGINT:
3179 if (iflag || minusc || sflag == 0)
3180 action = S_CATCH;
3181 break;
3182 case SIGQUIT:
3183#if DEBUG
3184 if (debug)
3185 break;
3186#endif
3187 /* FALLTHROUGH */
3188 case SIGTERM:
3189 if (iflag)
3190 action = S_IGN;
3191 break;
3192#if JOBS
3193 case SIGTSTP:
3194 case SIGTTOU:
3195 if (mflag)
3196 action = S_IGN;
3197 break;
3198#endif
3199 }
3200 }
3201
3202 t = &sigmode[signo - 1];
3203 tsig = *t;
3204 if (tsig == 0) {
3205 /*
3206 * current setting unknown
3207 */
3208 if (sigaction(signo, 0, &act) == -1) {
3209 /*
3210 * Pretend it worked; maybe we should give a warning
3211 * here, but other shells don't. We don't alter
3212 * sigmode, so that we retry every time.
3213 */
3214 return;
3215 }
3216 if (act.sa_handler == SIG_IGN) {
3217 if (mflag
3218 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3219 ) {
3220 tsig = S_IGN; /* don't hard ignore these */
3221 } else
3222 tsig = S_HARD_IGN;
3223 } else {
3224 tsig = S_RESET; /* force to be set */
3225 }
3226 }
3227 if (tsig == S_HARD_IGN || tsig == action)
3228 return;
3229 switch (action) {
3230 case S_CATCH:
3231 act.sa_handler = onsig;
3232 break;
3233 case S_IGN:
3234 act.sa_handler = SIG_IGN;
3235 break;
3236 default:
3237 act.sa_handler = SIG_DFL;
3238 }
3239 *t = action;
3240 act.sa_flags = 0;
3241 sigfillset(&act.sa_mask);
3242 sigaction(signo, &act, 0);
3243}
3244
3245/* mode flags for set_curjob */
3246#define CUR_DELETE 2
3247#define CUR_RUNNING 1
3248#define CUR_STOPPED 0
3249
3250/* mode flags for dowait */
3251#define DOWAIT_NORMAL 0
3252#define DOWAIT_BLOCK 1
3253
3254#if JOBS
3255/* pgrp of shell on invocation */
3256static int initialpgrp;
3257static int ttyfd = -1;
3258#endif
3259/* array of jobs */
3260static struct job *jobtab;
3261/* size of array */
3262static unsigned njobs;
3263/* current job */
3264static struct job *curjob;
3265/* number of presumed living untracked jobs */
3266static int jobless;
3267
3268static void
3269set_curjob(struct job *jp, unsigned mode)
3270{
3271 struct job *jp1;
3272 struct job **jpp, **curp;
3273
3274 /* first remove from list */
3275 jpp = curp = &curjob;
3276 do {
3277 jp1 = *jpp;
3278 if (jp1 == jp)
3279 break;
3280 jpp = &jp1->prev_job;
3281 } while (1);
3282 *jpp = jp1->prev_job;
3283
3284 /* Then re-insert in correct position */
3285 jpp = curp;
3286 switch (mode) {
3287 default:
3288#if DEBUG
3289 abort();
3290#endif
3291 case CUR_DELETE:
3292 /* job being deleted */
3293 break;
3294 case CUR_RUNNING:
3295 /* newly created job or backgrounded job,
3296 put after all stopped jobs. */
3297 do {
3298 jp1 = *jpp;
3299#if JOBS
3300 if (!jp1 || jp1->state != JOBSTOPPED)
3301#endif
3302 break;
3303 jpp = &jp1->prev_job;
3304 } while (1);
3305 /* FALLTHROUGH */
3306#if JOBS
3307 case CUR_STOPPED:
3308#endif
3309 /* newly stopped job - becomes curjob */
3310 jp->prev_job = *jpp;
3311 *jpp = jp;
3312 break;
3313 }
3314}
3315
3316#if JOBS || DEBUG
3317static int
3318jobno(const struct job *jp)
3319{
3320 return jp - jobtab + 1;
3321}
3322#endif
3323
3324/*
3325 * Convert a job name to a job structure.
3326 */
3327static struct job *
3328getjob(const char *name, int getctl)
3329{
3330 struct job *jp;
3331 struct job *found;
3332 const char *err_msg = "No such job: %s";
3333 unsigned num;
3334 int c;
3335 const char *p;
3336 char *(*match)(const char *, const char *);
3337
3338 jp = curjob;
3339 p = name;
3340 if (!p)
3341 goto currentjob;
3342
3343 if (*p != '%')
3344 goto err;
3345
3346 c = *++p;
3347 if (!c)
3348 goto currentjob;
3349
3350 if (!p[1]) {
3351 if (c == '+' || c == '%') {
3352 currentjob:
3353 err_msg = "No current job";
3354 goto check;
3355 }
3356 if (c == '-') {
3357 if (jp)
3358 jp = jp->prev_job;
3359 err_msg = "No previous job";
3360 check:
3361 if (!jp)
3362 goto err;
3363 goto gotit;
3364 }
3365 }
3366
3367 if (is_number(p)) {
3368 num = atoi(p);
3369 if (num < njobs) {
3370 jp = jobtab + num - 1;
3371 if (jp->used)
3372 goto gotit;
3373 goto err;
3374 }
3375 }
3376
3377 match = prefix;
3378 if (*p == '?') {
3379 match = strstr;
3380 p++;
3381 }
3382
3383 found = 0;
3384 while (1) {
3385 if (!jp)
3386 goto err;
3387 if (match(jp->ps[0].cmd, p)) {
3388 if (found)
3389 goto err;
3390 found = jp;
3391 err_msg = "%s: ambiguous";
3392 }
3393 jp = jp->prev_job;
3394 }
3395
3396 gotit:
3397#if JOBS
3398 err_msg = "job %s not created under job control";
3399 if (getctl && jp->jobctl == 0)
3400 goto err;
3401#endif
3402 return jp;
3403 err:
3404 ash_msg_and_raise_error(err_msg, name);
3405}
3406
3407/*
3408 * Mark a job structure as unused.
3409 */
3410static void
3411freejob(struct job *jp)
3412{
3413 struct procstat *ps;
3414 int i;
3415
3416 INT_OFF;
3417 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3418 if (ps->cmd != nullstr)
3419 free(ps->cmd);
3420 }
3421 if (jp->ps != &jp->ps0)
3422 free(jp->ps);
3423 jp->used = 0;
3424 set_curjob(jp, CUR_DELETE);
3425 INT_ON;
3426}
3427
3428#if JOBS
3429static void
3430xtcsetpgrp(int fd, pid_t pgrp)
3431{
3432 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003433 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003434}
3435
3436/*
3437 * Turn job control on and off.
3438 *
3439 * Note: This code assumes that the third arg to ioctl is a character
3440 * pointer, which is true on Berkeley systems but not System V. Since
3441 * System V doesn't have job control yet, this isn't a problem now.
3442 *
3443 * Called with interrupts off.
3444 */
3445static void
3446setjobctl(int on)
3447{
3448 int fd;
3449 int pgrp;
3450
3451 if (on == jobctl || rootshell == 0)
3452 return;
3453 if (on) {
3454 int ofd;
3455 ofd = fd = open(_PATH_TTY, O_RDWR);
3456 if (fd < 0) {
3457 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3458 * That sometimes helps to acquire controlling tty.
3459 * Obviously, a workaround for bugs when someone
3460 * failed to provide a controlling tty to bash! :) */
3461 fd += 3;
3462 while (!isatty(fd) && --fd >= 0)
3463 ;
3464 }
3465 fd = fcntl(fd, F_DUPFD, 10);
3466 close(ofd);
3467 if (fd < 0)
3468 goto out;
3469 fcntl(fd, F_SETFD, FD_CLOEXEC);
3470 do { /* while we are in the background */
3471 pgrp = tcgetpgrp(fd);
3472 if (pgrp < 0) {
3473 out:
3474 ash_msg("can't access tty; job control turned off");
3475 mflag = on = 0;
3476 goto close;
3477 }
3478 if (pgrp == getpgrp())
3479 break;
3480 killpg(0, SIGTTIN);
3481 } while (1);
3482 initialpgrp = pgrp;
3483
3484 setsignal(SIGTSTP);
3485 setsignal(SIGTTOU);
3486 setsignal(SIGTTIN);
3487 pgrp = rootpid;
3488 setpgid(0, pgrp);
3489 xtcsetpgrp(fd, pgrp);
3490 } else {
3491 /* turning job control off */
3492 fd = ttyfd;
3493 pgrp = initialpgrp;
3494 xtcsetpgrp(fd, pgrp);
3495 setpgid(0, pgrp);
3496 setsignal(SIGTSTP);
3497 setsignal(SIGTTOU);
3498 setsignal(SIGTTIN);
3499 close:
3500 close(fd);
3501 fd = -1;
3502 }
3503 ttyfd = fd;
3504 jobctl = on;
3505}
3506
3507static int
3508killcmd(int argc, char **argv)
3509{
3510 int signo = -1;
3511 int list = 0;
3512 int i;
3513 pid_t pid;
3514 struct job *jp;
3515
3516 if (argc <= 1) {
3517 usage:
3518 ash_msg_and_raise_error(
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003519"usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003520"kill -l [exitstatus]"
3521 );
3522 }
3523
3524 if (**++argv == '-') {
3525 signo = get_signum(*argv + 1);
3526 if (signo < 0) {
3527 int c;
3528
3529 while ((c = nextopt("ls:")) != '\0') {
3530 switch (c) {
3531 default:
3532#if DEBUG
3533 abort();
3534#endif
3535 case 'l':
3536 list = 1;
3537 break;
3538 case 's':
3539 signo = get_signum(optionarg);
3540 if (signo < 0) {
3541 ash_msg_and_raise_error(
3542 "invalid signal number or name: %s",
3543 optionarg
3544 );
3545 }
3546 break;
3547 }
3548 }
3549 argv = argptr;
3550 } else
3551 argv++;
3552 }
3553
3554 if (!list && signo < 0)
3555 signo = SIGTERM;
3556
3557 if ((signo < 0 || !*argv) ^ list) {
3558 goto usage;
3559 }
3560
3561 if (list) {
3562 const char *name;
3563
3564 if (!*argv) {
3565 for (i = 1; i < NSIG; i++) {
3566 name = get_signame(i);
3567 if (isdigit(*name))
3568 out1fmt(snlfmt, name);
3569 }
3570 return 0;
3571 }
3572 name = get_signame(signo);
3573 if (!isdigit(*name))
3574 ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
3575 out1fmt(snlfmt, name);
3576 return 0;
3577 }
3578
3579 i = 0;
3580 do {
3581 if (**argv == '%') {
3582 jp = getjob(*argv, 0);
3583 pid = -jp->ps[0].pid;
3584 } else {
3585 pid = **argv == '-' ?
3586 -number(*argv + 1) : number(*argv);
3587 }
3588 if (kill(pid, signo) != 0) {
3589 ash_msg("(%d) - %m", pid);
3590 i = 1;
3591 }
3592 } while (*++argv);
3593
3594 return i;
3595}
3596
3597static void
3598showpipe(struct job *jp, FILE *out)
3599{
3600 struct procstat *sp;
3601 struct procstat *spend;
3602
3603 spend = jp->ps + jp->nprocs;
3604 for (sp = jp->ps + 1; sp < spend; sp++)
3605 fprintf(out, " | %s", sp->cmd);
3606 outcslow('\n', out);
3607 flush_stdout_stderr();
3608}
3609
3610
3611static int
3612restartjob(struct job *jp, int mode)
3613{
3614 struct procstat *ps;
3615 int i;
3616 int status;
3617 pid_t pgid;
3618
3619 INT_OFF;
3620 if (jp->state == JOBDONE)
3621 goto out;
3622 jp->state = JOBRUNNING;
3623 pgid = jp->ps->pid;
3624 if (mode == FORK_FG)
3625 xtcsetpgrp(ttyfd, pgid);
3626 killpg(pgid, SIGCONT);
3627 ps = jp->ps;
3628 i = jp->nprocs;
3629 do {
3630 if (WIFSTOPPED(ps->status)) {
3631 ps->status = -1;
3632 }
3633 } while (ps++, --i);
3634 out:
3635 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3636 INT_ON;
3637 return status;
3638}
3639
3640static int
3641fg_bgcmd(int argc, char **argv)
3642{
3643 struct job *jp;
3644 FILE *out;
3645 int mode;
3646 int retval;
3647
3648 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3649 nextopt(nullstr);
3650 argv = argptr;
3651 out = stdout;
3652 do {
3653 jp = getjob(*argv, 1);
3654 if (mode == FORK_BG) {
3655 set_curjob(jp, CUR_RUNNING);
3656 fprintf(out, "[%d] ", jobno(jp));
3657 }
3658 outstr(jp->ps->cmd, out);
3659 showpipe(jp, out);
3660 retval = restartjob(jp, mode);
3661 } while (*argv && *++argv);
3662 return retval;
3663}
3664#endif
3665
3666static int
3667sprint_status(char *s, int status, int sigonly)
3668{
3669 int col;
3670 int st;
3671
3672 col = 0;
3673 if (!WIFEXITED(status)) {
3674#if JOBS
3675 if (WIFSTOPPED(status))
3676 st = WSTOPSIG(status);
3677 else
3678#endif
3679 st = WTERMSIG(status);
3680 if (sigonly) {
3681 if (st == SIGINT || st == SIGPIPE)
3682 goto out;
3683#if JOBS
3684 if (WIFSTOPPED(status))
3685 goto out;
3686#endif
3687 }
3688 st &= 0x7f;
3689 col = fmtstr(s, 32, strsignal(st));
3690 if (WCOREDUMP(status)) {
3691 col += fmtstr(s + col, 16, " (core dumped)");
3692 }
3693 } else if (!sigonly) {
3694 st = WEXITSTATUS(status);
3695 if (st)
3696 col = fmtstr(s, 16, "Done(%d)", st);
3697 else
3698 col = fmtstr(s, 16, "Done");
3699 }
3700 out:
3701 return col;
3702}
3703
3704/*
3705 * Do a wait system call. If job control is compiled in, we accept
3706 * stopped processes. If block is zero, we return a value of zero
3707 * rather than blocking.
3708 *
3709 * System V doesn't have a non-blocking wait system call. It does
3710 * have a SIGCLD signal that is sent to a process when one of it's
3711 * children dies. The obvious way to use SIGCLD would be to install
3712 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3713 * was received, and have waitproc bump another counter when it got
3714 * the status of a process. Waitproc would then know that a wait
3715 * system call would not block if the two counters were different.
3716 * This approach doesn't work because if a process has children that
3717 * have not been waited for, System V will send it a SIGCLD when it
3718 * installs a signal handler for SIGCLD. What this means is that when
3719 * a child exits, the shell will be sent SIGCLD signals continuously
3720 * until is runs out of stack space, unless it does a wait call before
3721 * restoring the signal handler. The code below takes advantage of
3722 * this (mis)feature by installing a signal handler for SIGCLD and
3723 * then checking to see whether it was called. If there are any
3724 * children to be waited for, it will be.
3725 *
3726 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3727 * waits at all. In this case, the user will not be informed when
3728 * a background process until the next time she runs a real program
3729 * (as opposed to running a builtin command or just typing return),
3730 * and the jobs command may give out of date information.
3731 */
3732static int
3733waitproc(int block, int *status)
3734{
3735 int flags = 0;
3736
3737#if JOBS
3738 if (jobctl)
3739 flags |= WUNTRACED;
3740#endif
3741 if (block == 0)
3742 flags |= WNOHANG;
3743 return wait3(status, flags, (struct rusage *)NULL);
3744}
3745
3746/*
3747 * Wait for a process to terminate.
3748 */
3749static int
3750dowait(int block, struct job *job)
3751{
3752 int pid;
3753 int status;
3754 struct job *jp;
3755 struct job *thisjob;
3756 int state;
3757
3758 TRACE(("dowait(%d) called\n", block));
3759 pid = waitproc(block, &status);
3760 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3761 if (pid <= 0)
3762 return pid;
3763 INT_OFF;
3764 thisjob = NULL;
3765 for (jp = curjob; jp; jp = jp->prev_job) {
3766 struct procstat *sp;
3767 struct procstat *spend;
3768 if (jp->state == JOBDONE)
3769 continue;
3770 state = JOBDONE;
3771 spend = jp->ps + jp->nprocs;
3772 sp = jp->ps;
3773 do {
3774 if (sp->pid == pid) {
3775 TRACE(("Job %d: changing status of proc %d "
3776 "from 0x%x to 0x%x\n",
3777 jobno(jp), pid, sp->status, status));
3778 sp->status = status;
3779 thisjob = jp;
3780 }
3781 if (sp->status == -1)
3782 state = JOBRUNNING;
3783#if JOBS
3784 if (state == JOBRUNNING)
3785 continue;
3786 if (WIFSTOPPED(sp->status)) {
3787 jp->stopstatus = sp->status;
3788 state = JOBSTOPPED;
3789 }
3790#endif
3791 } while (++sp < spend);
3792 if (thisjob)
3793 goto gotjob;
3794 }
3795#if JOBS
3796 if (!WIFSTOPPED(status))
3797#endif
3798
3799 jobless--;
3800 goto out;
3801
3802 gotjob:
3803 if (state != JOBRUNNING) {
3804 thisjob->changed = 1;
3805
3806 if (thisjob->state != state) {
3807 TRACE(("Job %d: changing state from %d to %d\n",
3808 jobno(thisjob), thisjob->state, state));
3809 thisjob->state = state;
3810#if JOBS
3811 if (state == JOBSTOPPED) {
3812 set_curjob(thisjob, CUR_STOPPED);
3813 }
3814#endif
3815 }
3816 }
3817
3818 out:
3819 INT_ON;
3820
3821 if (thisjob && thisjob == job) {
3822 char s[48 + 1];
3823 int len;
3824
3825 len = sprint_status(s, status, 1);
3826 if (len) {
3827 s[len] = '\n';
3828 s[len + 1] = 0;
3829 out2str(s);
3830 }
3831 }
3832 return pid;
3833}
3834
3835#if JOBS
3836static void
3837showjob(FILE *out, struct job *jp, int mode)
3838{
3839 struct procstat *ps;
3840 struct procstat *psend;
3841 int col;
3842 int indent;
3843 char s[80];
3844
3845 ps = jp->ps;
3846
3847 if (mode & SHOW_PGID) {
3848 /* just output process (group) id of pipeline */
3849 fprintf(out, "%d\n", ps->pid);
3850 return;
3851 }
3852
3853 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3854 indent = col;
3855
3856 if (jp == curjob)
3857 s[col - 2] = '+';
3858 else if (curjob && jp == curjob->prev_job)
3859 s[col - 2] = '-';
3860
3861 if (mode & SHOW_PID)
3862 col += fmtstr(s + col, 16, "%d ", ps->pid);
3863
3864 psend = ps + jp->nprocs;
3865
3866 if (jp->state == JOBRUNNING) {
3867 strcpy(s + col, "Running");
3868 col += sizeof("Running") - 1;
3869 } else {
3870 int status = psend[-1].status;
3871 if (jp->state == JOBSTOPPED)
3872 status = jp->stopstatus;
3873 col += sprint_status(s + col, status, 0);
3874 }
3875
3876 goto start;
3877
3878 do {
3879 /* for each process */
3880 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
3881 start:
3882 fprintf(out, "%s%*c%s",
3883 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3884 );
3885 if (!(mode & SHOW_PID)) {
3886 showpipe(jp, out);
3887 break;
3888 }
3889 if (++ps == psend) {
3890 outcslow('\n', out);
3891 break;
3892 }
3893 } while (1);
3894
3895 jp->changed = 0;
3896
3897 if (jp->state == JOBDONE) {
3898 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3899 freejob(jp);
3900 }
3901}
3902
3903static int
3904jobscmd(int argc, char **argv)
3905{
3906 int mode, m;
3907 FILE *out;
3908
3909 mode = 0;
3910 while ((m = nextopt("lp"))) {
3911 if (m == 'l')
3912 mode = SHOW_PID;
3913 else
3914 mode = SHOW_PGID;
3915 }
3916
3917 out = stdout;
3918 argv = argptr;
3919 if (*argv) {
3920 do
3921 showjob(out, getjob(*argv,0), mode);
3922 while (*++argv);
3923 } else
3924 showjobs(out, mode);
3925
3926 return 0;
3927}
3928
3929/*
3930 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3931 * statuses have changed since the last call to showjobs.
3932 */
3933static void
3934showjobs(FILE *out, int mode)
3935{
3936 struct job *jp;
3937
3938 TRACE(("showjobs(%x) called\n", mode));
3939
3940 /* If not even one one job changed, there is nothing to do */
3941 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3942 continue;
3943
3944 for (jp = curjob; jp; jp = jp->prev_job) {
3945 if (!(mode & SHOW_CHANGED) || jp->changed)
3946 showjob(out, jp, mode);
3947 }
3948}
3949#endif /* JOBS */
3950
3951static int
3952getstatus(struct job *job)
3953{
3954 int status;
3955 int retval;
3956
3957 status = job->ps[job->nprocs - 1].status;
3958 retval = WEXITSTATUS(status);
3959 if (!WIFEXITED(status)) {
3960#if JOBS
3961 retval = WSTOPSIG(status);
3962 if (!WIFSTOPPED(status))
3963#endif
3964 {
3965 /* XXX: limits number of signals */
3966 retval = WTERMSIG(status);
3967#if JOBS
3968 if (retval == SIGINT)
3969 job->sigint = 1;
3970#endif
3971 }
3972 retval += 128;
3973 }
3974 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3975 jobno(job), job->nprocs, status, retval));
3976 return retval;
3977}
3978
3979static int
3980waitcmd(int argc, char **argv)
3981{
3982 struct job *job;
3983 int retval;
3984 struct job *jp;
3985
3986 EXSIGON;
3987
3988 nextopt(nullstr);
3989 retval = 0;
3990
3991 argv = argptr;
3992 if (!*argv) {
3993 /* wait for all jobs */
3994 for (;;) {
3995 jp = curjob;
3996 while (1) {
3997 if (!jp) {
3998 /* no running procs */
3999 goto out;
4000 }
4001 if (jp->state == JOBRUNNING)
4002 break;
4003 jp->waited = 1;
4004 jp = jp->prev_job;
4005 }
4006 dowait(DOWAIT_BLOCK, 0);
4007 }
4008 }
4009
4010 retval = 127;
4011 do {
4012 if (**argv != '%') {
4013 pid_t pid = number(*argv);
4014 job = curjob;
4015 goto start;
4016 do {
4017 if (job->ps[job->nprocs - 1].pid == pid)
4018 break;
4019 job = job->prev_job;
4020 start:
4021 if (!job)
4022 goto repeat;
4023 } while (1);
4024 } else
4025 job = getjob(*argv, 0);
4026 /* loop until process terminated or stopped */
4027 while (job->state == JOBRUNNING)
4028 dowait(DOWAIT_BLOCK, 0);
4029 job->waited = 1;
4030 retval = getstatus(job);
4031 repeat:
4032 ;
4033 } while (*++argv);
4034
4035 out:
4036 return retval;
4037}
4038
4039static struct job *
4040growjobtab(void)
4041{
4042 size_t len;
4043 ptrdiff_t offset;
4044 struct job *jp, *jq;
4045
4046 len = njobs * sizeof(*jp);
4047 jq = jobtab;
4048 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4049
4050 offset = (char *)jp - (char *)jq;
4051 if (offset) {
4052 /* Relocate pointers */
4053 size_t l = len;
4054
4055 jq = (struct job *)((char *)jq + l);
4056 while (l) {
4057 l -= sizeof(*jp);
4058 jq--;
4059#define joff(p) ((struct job *)((char *)(p) + l))
4060#define jmove(p) (p) = (void *)((char *)(p) + offset)
4061 if (joff(jp)->ps == &jq->ps0)
4062 jmove(joff(jp)->ps);
4063 if (joff(jp)->prev_job)
4064 jmove(joff(jp)->prev_job);
4065 }
4066 if (curjob)
4067 jmove(curjob);
4068#undef joff
4069#undef jmove
4070 }
4071
4072 njobs += 4;
4073 jobtab = jp;
4074 jp = (struct job *)((char *)jp + len);
4075 jq = jp + 3;
4076 do {
4077 jq->used = 0;
4078 } while (--jq >= jp);
4079 return jp;
4080}
4081
4082/*
4083 * Return a new job structure.
4084 * Called with interrupts off.
4085 */
4086static struct job *
4087makejob(union node *node, int nprocs)
4088{
4089 int i;
4090 struct job *jp;
4091
4092 for (i = njobs, jp = jobtab; ; jp++) {
4093 if (--i < 0) {
4094 jp = growjobtab();
4095 break;
4096 }
4097 if (jp->used == 0)
4098 break;
4099 if (jp->state != JOBDONE || !jp->waited)
4100 continue;
4101#if JOBS
4102 if (jobctl)
4103 continue;
4104#endif
4105 freejob(jp);
4106 break;
4107 }
4108 memset(jp, 0, sizeof(*jp));
4109#if JOBS
4110 if (jobctl)
4111 jp->jobctl = 1;
4112#endif
4113 jp->prev_job = curjob;
4114 curjob = jp;
4115 jp->used = 1;
4116 jp->ps = &jp->ps0;
4117 if (nprocs > 1) {
4118 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4119 }
4120 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4121 jobno(jp)));
4122 return jp;
4123}
4124
4125#if JOBS
4126/*
4127 * Return a string identifying a command (to be printed by the
4128 * jobs command).
4129 */
4130static char *cmdnextc;
4131
4132static void
4133cmdputs(const char *s)
4134{
4135 const char *p, *str;
4136 char c, cc[2] = " ";
4137 char *nextc;
4138 int subtype = 0;
4139 int quoted = 0;
4140 static const char vstype[VSTYPE + 1][4] = {
4141 "", "}", "-", "+", "?", "=",
4142 "%", "%%", "#", "##"
4143 };
4144
4145 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4146 p = s;
4147 while ((c = *p++) != 0) {
4148 str = 0;
4149 switch (c) {
4150 case CTLESC:
4151 c = *p++;
4152 break;
4153 case CTLVAR:
4154 subtype = *p++;
4155 if ((subtype & VSTYPE) == VSLENGTH)
4156 str = "${#";
4157 else
4158 str = "${";
4159 if (!(subtype & VSQUOTE) == !(quoted & 1))
4160 goto dostr;
4161 quoted ^= 1;
4162 c = '"';
4163 break;
4164 case CTLENDVAR:
4165 str = "\"}" + !(quoted & 1);
4166 quoted >>= 1;
4167 subtype = 0;
4168 goto dostr;
4169 case CTLBACKQ:
4170 str = "$(...)";
4171 goto dostr;
4172 case CTLBACKQ+CTLQUOTE:
4173 str = "\"$(...)\"";
4174 goto dostr;
4175#if ENABLE_ASH_MATH_SUPPORT
4176 case CTLARI:
4177 str = "$((";
4178 goto dostr;
4179 case CTLENDARI:
4180 str = "))";
4181 goto dostr;
4182#endif
4183 case CTLQUOTEMARK:
4184 quoted ^= 1;
4185 c = '"';
4186 break;
4187 case '=':
4188 if (subtype == 0)
4189 break;
4190 if ((subtype & VSTYPE) != VSNORMAL)
4191 quoted <<= 1;
4192 str = vstype[subtype & VSTYPE];
4193 if (subtype & VSNUL)
4194 c = ':';
4195 else
4196 goto checkstr;
4197 break;
4198 case '\'':
4199 case '\\':
4200 case '"':
4201 case '$':
4202 /* These can only happen inside quotes */
4203 cc[0] = c;
4204 str = cc;
4205 c = '\\';
4206 break;
4207 default:
4208 break;
4209 }
4210 USTPUTC(c, nextc);
4211 checkstr:
4212 if (!str)
4213 continue;
4214 dostr:
4215 while ((c = *str++)) {
4216 USTPUTC(c, nextc);
4217 }
4218 }
4219 if (quoted & 1) {
4220 USTPUTC('"', nextc);
4221 }
4222 *nextc = 0;
4223 cmdnextc = nextc;
4224}
4225
4226/* cmdtxt() and cmdlist() call each other */
4227static void cmdtxt(union node *n);
4228
4229static void
4230cmdlist(union node *np, int sep)
4231{
4232 for (; np; np = np->narg.next) {
4233 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004234 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004235 cmdtxt(np);
4236 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004237 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004238 }
4239}
4240
4241static void
4242cmdtxt(union node *n)
4243{
4244 union node *np;
4245 struct nodelist *lp;
4246 const char *p;
4247 char s[2];
4248
4249 if (!n)
4250 return;
4251 switch (n->type) {
4252 default:
4253#if DEBUG
4254 abort();
4255#endif
4256 case NPIPE:
4257 lp = n->npipe.cmdlist;
4258 for (;;) {
4259 cmdtxt(lp->n);
4260 lp = lp->next;
4261 if (!lp)
4262 break;
4263 cmdputs(" | ");
4264 }
4265 break;
4266 case NSEMI:
4267 p = "; ";
4268 goto binop;
4269 case NAND:
4270 p = " && ";
4271 goto binop;
4272 case NOR:
4273 p = " || ";
4274 binop:
4275 cmdtxt(n->nbinary.ch1);
4276 cmdputs(p);
4277 n = n->nbinary.ch2;
4278 goto donode;
4279 case NREDIR:
4280 case NBACKGND:
4281 n = n->nredir.n;
4282 goto donode;
4283 case NNOT:
4284 cmdputs("!");
4285 n = n->nnot.com;
4286 donode:
4287 cmdtxt(n);
4288 break;
4289 case NIF:
4290 cmdputs("if ");
4291 cmdtxt(n->nif.test);
4292 cmdputs("; then ");
4293 n = n->nif.ifpart;
4294 if (n->nif.elsepart) {
4295 cmdtxt(n);
4296 cmdputs("; else ");
4297 n = n->nif.elsepart;
4298 }
4299 p = "; fi";
4300 goto dotail;
4301 case NSUBSHELL:
4302 cmdputs("(");
4303 n = n->nredir.n;
4304 p = ")";
4305 goto dotail;
4306 case NWHILE:
4307 p = "while ";
4308 goto until;
4309 case NUNTIL:
4310 p = "until ";
4311 until:
4312 cmdputs(p);
4313 cmdtxt(n->nbinary.ch1);
4314 n = n->nbinary.ch2;
4315 p = "; done";
4316 dodo:
4317 cmdputs("; do ");
4318 dotail:
4319 cmdtxt(n);
4320 goto dotail2;
4321 case NFOR:
4322 cmdputs("for ");
4323 cmdputs(n->nfor.var);
4324 cmdputs(" in ");
4325 cmdlist(n->nfor.args, 1);
4326 n = n->nfor.body;
4327 p = "; done";
4328 goto dodo;
4329 case NDEFUN:
4330 cmdputs(n->narg.text);
4331 p = "() { ... }";
4332 goto dotail2;
4333 case NCMD:
4334 cmdlist(n->ncmd.args, 1);
4335 cmdlist(n->ncmd.redirect, 0);
4336 break;
4337 case NARG:
4338 p = n->narg.text;
4339 dotail2:
4340 cmdputs(p);
4341 break;
4342 case NHERE:
4343 case NXHERE:
4344 p = "<<...";
4345 goto dotail2;
4346 case NCASE:
4347 cmdputs("case ");
4348 cmdputs(n->ncase.expr->narg.text);
4349 cmdputs(" in ");
4350 for (np = n->ncase.cases; np; np = np->nclist.next) {
4351 cmdtxt(np->nclist.pattern);
4352 cmdputs(") ");
4353 cmdtxt(np->nclist.body);
4354 cmdputs(";; ");
4355 }
4356 p = "esac";
4357 goto dotail2;
4358 case NTO:
4359 p = ">";
4360 goto redir;
4361 case NCLOBBER:
4362 p = ">|";
4363 goto redir;
4364 case NAPPEND:
4365 p = ">>";
4366 goto redir;
4367 case NTOFD:
4368 p = ">&";
4369 goto redir;
4370 case NFROM:
4371 p = "<";
4372 goto redir;
4373 case NFROMFD:
4374 p = "<&";
4375 goto redir;
4376 case NFROMTO:
4377 p = "<>";
4378 redir:
4379 s[0] = n->nfile.fd + '0';
4380 s[1] = '\0';
4381 cmdputs(s);
4382 cmdputs(p);
4383 if (n->type == NTOFD || n->type == NFROMFD) {
4384 s[0] = n->ndup.dupfd + '0';
4385 p = s;
4386 goto dotail2;
4387 }
4388 n = n->nfile.fname;
4389 goto donode;
4390 }
4391}
4392
4393static char *
4394commandtext(union node *n)
4395{
4396 char *name;
4397
4398 STARTSTACKSTR(cmdnextc);
4399 cmdtxt(n);
4400 name = stackblock();
4401 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4402 name, cmdnextc, cmdnextc));
4403 return ckstrdup(name);
4404}
4405#endif /* JOBS */
4406
4407/*
4408 * Fork off a subshell. If we are doing job control, give the subshell its
4409 * own process group. Jp is a job structure that the job is to be added to.
4410 * N is the command that will be evaluated by the child. Both jp and n may
4411 * be NULL. The mode parameter can be one of the following:
4412 * FORK_FG - Fork off a foreground process.
4413 * FORK_BG - Fork off a background process.
4414 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4415 * process group even if job control is on.
4416 *
4417 * When job control is turned off, background processes have their standard
4418 * input redirected to /dev/null (except for the second and later processes
4419 * in a pipeline).
4420 *
4421 * Called with interrupts off.
4422 */
4423/*
4424 * Clear traps on a fork.
4425 */
4426static void
4427clear_traps(void)
4428{
4429 char **tp;
4430
4431 for (tp = trap; tp < &trap[NSIG]; tp++) {
4432 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4433 INT_OFF;
4434 free(*tp);
4435 *tp = NULL;
4436 if (tp != &trap[0])
4437 setsignal(tp - trap);
4438 INT_ON;
4439 }
4440 }
4441}
4442/* lives far away from here, needed for forkchild */
4443static void closescript(void);
4444static void
4445forkchild(struct job *jp, union node *n, int mode)
4446{
4447 int oldlvl;
4448
4449 TRACE(("Child shell %d\n", getpid()));
4450 oldlvl = shlvl;
4451 shlvl++;
4452
4453 closescript();
4454 clear_traps();
4455#if JOBS
4456 /* do job control only in root shell */
4457 jobctl = 0;
4458 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4459 pid_t pgrp;
4460
4461 if (jp->nprocs == 0)
4462 pgrp = getpid();
4463 else
4464 pgrp = jp->ps[0].pid;
4465 /* This can fail because we are doing it in the parent also */
4466 (void)setpgid(0, pgrp);
4467 if (mode == FORK_FG)
4468 xtcsetpgrp(ttyfd, pgrp);
4469 setsignal(SIGTSTP);
4470 setsignal(SIGTTOU);
4471 } else
4472#endif
4473 if (mode == FORK_BG) {
4474 ignoresig(SIGINT);
4475 ignoresig(SIGQUIT);
4476 if (jp->nprocs == 0) {
4477 close(0);
4478 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004479 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004480 }
4481 }
4482 if (!oldlvl && iflag) {
4483 setsignal(SIGINT);
4484 setsignal(SIGQUIT);
4485 setsignal(SIGTERM);
4486 }
4487 for (jp = curjob; jp; jp = jp->prev_job)
4488 freejob(jp);
4489 jobless = 0;
4490}
4491
4492static void
4493forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4494{
4495 TRACE(("In parent shell: child = %d\n", pid));
4496 if (!jp) {
4497 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4498 jobless++;
4499 return;
4500 }
4501#if JOBS
4502 if (mode != FORK_NOJOB && jp->jobctl) {
4503 int pgrp;
4504
4505 if (jp->nprocs == 0)
4506 pgrp = pid;
4507 else
4508 pgrp = jp->ps[0].pid;
4509 /* This can fail because we are doing it in the child also */
4510 setpgid(pid, pgrp);
4511 }
4512#endif
4513 if (mode == FORK_BG) {
4514 backgndpid = pid; /* set $! */
4515 set_curjob(jp, CUR_RUNNING);
4516 }
4517 if (jp) {
4518 struct procstat *ps = &jp->ps[jp->nprocs++];
4519 ps->pid = pid;
4520 ps->status = -1;
4521 ps->cmd = nullstr;
4522#if JOBS
4523 if (jobctl && n)
4524 ps->cmd = commandtext(n);
4525#endif
4526 }
4527}
4528
4529static int
4530forkshell(struct job *jp, union node *n, int mode)
4531{
4532 int pid;
4533
4534 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4535 pid = fork();
4536 if (pid < 0) {
4537 TRACE(("Fork failed, errno=%d", errno));
4538 if (jp)
4539 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004540 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004541 }
4542 if (pid == 0)
4543 forkchild(jp, n, mode);
4544 else
4545 forkparent(jp, n, mode, pid);
4546 return pid;
4547}
4548
4549/*
4550 * Wait for job to finish.
4551 *
4552 * Under job control we have the problem that while a child process is
4553 * running interrupts generated by the user are sent to the child but not
4554 * to the shell. This means that an infinite loop started by an inter-
4555 * active user may be hard to kill. With job control turned off, an
4556 * interactive user may place an interactive program inside a loop. If
4557 * the interactive program catches interrupts, the user doesn't want
4558 * these interrupts to also abort the loop. The approach we take here
4559 * is to have the shell ignore interrupt signals while waiting for a
4560 * foreground process to terminate, and then send itself an interrupt
4561 * signal if the child process was terminated by an interrupt signal.
4562 * Unfortunately, some programs want to do a bit of cleanup and then
4563 * exit on interrupt; unless these processes terminate themselves by
4564 * sending a signal to themselves (instead of calling exit) they will
4565 * confuse this approach.
4566 *
4567 * Called with interrupts off.
4568 */
4569static int
4570waitforjob(struct job *jp)
4571{
4572 int st;
4573
4574 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4575 while (jp->state == JOBRUNNING) {
4576 dowait(DOWAIT_BLOCK, jp);
4577 }
4578 st = getstatus(jp);
4579#if JOBS
4580 if (jp->jobctl) {
4581 xtcsetpgrp(ttyfd, rootpid);
4582 /*
4583 * This is truly gross.
4584 * If we're doing job control, then we did a TIOCSPGRP which
4585 * caused us (the shell) to no longer be in the controlling
4586 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4587 * intuit from the subprocess exit status whether a SIGINT
4588 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4589 */
4590 if (jp->sigint)
4591 raise(SIGINT);
4592 }
4593 if (jp->state == JOBDONE)
4594#endif
4595 freejob(jp);
4596 return st;
4597}
4598
4599/*
4600 * return 1 if there are stopped jobs, otherwise 0
4601 */
4602static int
4603stoppedjobs(void)
4604{
4605 struct job *jp;
4606 int retval;
4607
4608 retval = 0;
4609 if (job_warning)
4610 goto out;
4611 jp = curjob;
4612 if (jp && jp->state == JOBSTOPPED) {
4613 out2str("You have stopped jobs.\n");
4614 job_warning = 2;
4615 retval++;
4616 }
4617 out:
4618 return retval;
4619}
4620
4621
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004622/* ============ redir.c
4623 *
4624 * Code for dealing with input/output redirection.
4625 */
4626
4627#define EMPTY -2 /* marks an unused slot in redirtab */
4628#ifndef PIPE_BUF
4629# define PIPESIZE 4096 /* amount of buffering in a pipe */
4630#else
4631# define PIPESIZE PIPE_BUF
4632#endif
4633
4634/*
4635 * Open a file in noclobber mode.
4636 * The code was copied from bash.
4637 */
4638static int
4639noclobberopen(const char *fname)
4640{
4641 int r, fd;
4642 struct stat finfo, finfo2;
4643
4644 /*
4645 * If the file exists and is a regular file, return an error
4646 * immediately.
4647 */
4648 r = stat(fname, &finfo);
4649 if (r == 0 && S_ISREG(finfo.st_mode)) {
4650 errno = EEXIST;
4651 return -1;
4652 }
4653
4654 /*
4655 * If the file was not present (r != 0), make sure we open it
4656 * exclusively so that if it is created before we open it, our open
4657 * will fail. Make sure that we do not truncate an existing file.
4658 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4659 * file was not a regular file, we leave O_EXCL off.
4660 */
4661 if (r != 0)
4662 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4663 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4664
4665 /* If the open failed, return the file descriptor right away. */
4666 if (fd < 0)
4667 return fd;
4668
4669 /*
4670 * OK, the open succeeded, but the file may have been changed from a
4671 * non-regular file to a regular file between the stat and the open.
4672 * We are assuming that the O_EXCL open handles the case where FILENAME
4673 * did not exist and is symlinked to an existing file between the stat
4674 * and open.
4675 */
4676
4677 /*
4678 * If we can open it and fstat the file descriptor, and neither check
4679 * revealed that it was a regular file, and the file has not been
4680 * replaced, return the file descriptor.
4681 */
4682 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4683 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4684 return fd;
4685
4686 /* The file has been replaced. badness. */
4687 close(fd);
4688 errno = EEXIST;
4689 return -1;
4690}
4691
4692/*
4693 * Handle here documents. Normally we fork off a process to write the
4694 * data to a pipe. If the document is short, we can stuff the data in
4695 * the pipe without forking.
4696 */
4697/* openhere needs this forward reference */
4698static void expandhere(union node *arg, int fd);
4699static int
4700openhere(union node *redir)
4701{
4702 int pip[2];
4703 size_t len = 0;
4704
4705 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004706 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004707 if (redir->type == NHERE) {
4708 len = strlen(redir->nhere.doc->narg.text);
4709 if (len <= PIPESIZE) {
4710 full_write(pip[1], redir->nhere.doc->narg.text, len);
4711 goto out;
4712 }
4713 }
4714 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4715 close(pip[0]);
4716 signal(SIGINT, SIG_IGN);
4717 signal(SIGQUIT, SIG_IGN);
4718 signal(SIGHUP, SIG_IGN);
4719#ifdef SIGTSTP
4720 signal(SIGTSTP, SIG_IGN);
4721#endif
4722 signal(SIGPIPE, SIG_DFL);
4723 if (redir->type == NHERE)
4724 full_write(pip[1], redir->nhere.doc->narg.text, len);
4725 else
4726 expandhere(redir->nhere.doc, pip[1]);
4727 _exit(0);
4728 }
4729 out:
4730 close(pip[1]);
4731 return pip[0];
4732}
4733
4734static int
4735openredirect(union node *redir)
4736{
4737 char *fname;
4738 int f;
4739
4740 switch (redir->nfile.type) {
4741 case NFROM:
4742 fname = redir->nfile.expfname;
4743 f = open(fname, O_RDONLY);
4744 if (f < 0)
4745 goto eopen;
4746 break;
4747 case NFROMTO:
4748 fname = redir->nfile.expfname;
4749 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4750 if (f < 0)
4751 goto ecreate;
4752 break;
4753 case NTO:
4754 /* Take care of noclobber mode. */
4755 if (Cflag) {
4756 fname = redir->nfile.expfname;
4757 f = noclobberopen(fname);
4758 if (f < 0)
4759 goto ecreate;
4760 break;
4761 }
4762 /* FALLTHROUGH */
4763 case NCLOBBER:
4764 fname = redir->nfile.expfname;
4765 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4766 if (f < 0)
4767 goto ecreate;
4768 break;
4769 case NAPPEND:
4770 fname = redir->nfile.expfname;
4771 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4772 if (f < 0)
4773 goto ecreate;
4774 break;
4775 default:
4776#if DEBUG
4777 abort();
4778#endif
4779 /* Fall through to eliminate warning. */
4780 case NTOFD:
4781 case NFROMFD:
4782 f = -1;
4783 break;
4784 case NHERE:
4785 case NXHERE:
4786 f = openhere(redir);
4787 break;
4788 }
4789
4790 return f;
4791 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004792 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004793 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004794 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004795}
4796
4797/*
4798 * Copy a file descriptor to be >= to. Returns -1
4799 * if the source file descriptor is closed, EMPTY if there are no unused
4800 * file descriptors left.
4801 */
4802static int
4803copyfd(int from, int to)
4804{
4805 int newfd;
4806
4807 newfd = fcntl(from, F_DUPFD, to);
4808 if (newfd < 0) {
4809 if (errno == EMFILE)
4810 return EMPTY;
4811 ash_msg_and_raise_error("%d: %m", from);
4812 }
4813 return newfd;
4814}
4815
4816static void
4817dupredirect(union node *redir, int f)
4818{
4819 int fd = redir->nfile.fd;
4820
4821 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4822 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4823 copyfd(redir->ndup.dupfd, fd);
4824 }
4825 return;
4826 }
4827
4828 if (f != fd) {
4829 copyfd(f, fd);
4830 close(f);
4831 }
4832}
4833
4834/*
4835 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4836 * old file descriptors are stashed away so that the redirection can be
4837 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4838 * standard output, and the standard error if it becomes a duplicate of
4839 * stdout, is saved in memory.
4840 */
4841/* flags passed to redirect */
4842#define REDIR_PUSH 01 /* save previous values of file descriptors */
4843#define REDIR_SAVEFD2 03 /* set preverrout */
4844static void
4845redirect(union node *redir, int flags)
4846{
4847 union node *n;
4848 struct redirtab *sv;
4849 int i;
4850 int fd;
4851 int newfd;
4852 int *p;
4853 nullredirs++;
4854 if (!redir) {
4855 return;
4856 }
4857 sv = NULL;
4858 INT_OFF;
4859 if (flags & REDIR_PUSH) {
4860 struct redirtab *q;
4861 q = ckmalloc(sizeof(struct redirtab));
4862 q->next = redirlist;
4863 redirlist = q;
4864 q->nullredirs = nullredirs - 1;
4865 for (i = 0; i < 10; i++)
4866 q->renamed[i] = EMPTY;
4867 nullredirs = 0;
4868 sv = q;
4869 }
4870 n = redir;
4871 do {
4872 fd = n->nfile.fd;
4873 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4874 && n->ndup.dupfd == fd)
4875 continue; /* redirect from/to same file descriptor */
4876
4877 newfd = openredirect(n);
4878 if (fd == newfd)
4879 continue;
4880 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4881 i = fcntl(fd, F_DUPFD, 10);
4882
4883 if (i == -1) {
4884 i = errno;
4885 if (i != EBADF) {
4886 close(newfd);
4887 errno = i;
4888 ash_msg_and_raise_error("%d: %m", fd);
4889 /* NOTREACHED */
4890 }
4891 } else {
4892 *p = i;
4893 close(fd);
4894 }
4895 } else {
4896 close(fd);
4897 }
4898 dupredirect(n, newfd);
4899 } while ((n = n->nfile.next));
4900 INT_ON;
4901 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4902 preverrout_fd = sv->renamed[2];
4903}
4904
4905/*
4906 * Undo the effects of the last redirection.
4907 */
4908static void
4909popredir(int drop)
4910{
4911 struct redirtab *rp;
4912 int i;
4913
4914 if (--nullredirs >= 0)
4915 return;
4916 INT_OFF;
4917 rp = redirlist;
4918 for (i = 0; i < 10; i++) {
4919 if (rp->renamed[i] != EMPTY) {
4920 if (!drop) {
4921 close(i);
4922 copyfd(rp->renamed[i], i);
4923 }
4924 close(rp->renamed[i]);
4925 }
4926 }
4927 redirlist = rp->next;
4928 nullredirs = rp->nullredirs;
4929 free(rp);
4930 INT_ON;
4931}
4932
4933/*
4934 * Undo all redirections. Called on error or interrupt.
4935 */
4936
4937/*
4938 * Discard all saved file descriptors.
4939 */
4940static void
4941clearredir(int drop)
4942{
4943 for (;;) {
4944 nullredirs = 0;
4945 if (!redirlist)
4946 break;
4947 popredir(drop);
4948 }
4949}
4950
4951static int
4952redirectsafe(union node *redir, int flags)
4953{
4954 int err;
4955 volatile int saveint;
4956 struct jmploc *volatile savehandler = exception_handler;
4957 struct jmploc jmploc;
4958
4959 SAVE_INT(saveint);
4960 err = setjmp(jmploc.loc) * 2;
4961 if (!err) {
4962 exception_handler = &jmploc;
4963 redirect(redir, flags);
4964 }
4965 exception_handler = savehandler;
4966 if (err && exception != EXERROR)
4967 longjmp(exception_handler->loc, 1);
4968 RESTORE_INT(saveint);
4969 return err;
4970}
4971
4972
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004973/* ============ Routines to expand arguments to commands
4974 *
4975 * We have to deal with backquotes, shell variables, and file metacharacters.
4976 */
4977
4978/*
4979 * expandarg flags
4980 */
4981#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4982#define EXP_TILDE 0x2 /* do normal tilde expansion */
4983#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4984#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4985#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4986#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4987#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4988#define EXP_WORD 0x80 /* expand word in parameter expansion */
4989#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4990/*
4991 * _rmescape() flags
4992 */
4993#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4994#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4995#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4996#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4997#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4998
4999/*
5000 * Structure specifying which parts of the string should be searched
5001 * for IFS characters.
5002 */
5003struct ifsregion {
5004 struct ifsregion *next; /* next region in list */
5005 int begoff; /* offset of start of region */
5006 int endoff; /* offset of end of region */
5007 int nulonly; /* search for nul bytes only */
5008};
5009
5010struct arglist {
5011 struct strlist *list;
5012 struct strlist **lastp;
5013};
5014
5015/* output of current string */
5016static char *expdest;
5017/* list of back quote expressions */
5018static struct nodelist *argbackq;
5019/* first struct in list of ifs regions */
5020static struct ifsregion ifsfirst;
5021/* last struct in list */
5022static struct ifsregion *ifslastp;
5023/* holds expanded arg list */
5024static struct arglist exparg;
5025
5026/*
5027 * Our own itoa().
5028 */
5029static int
5030cvtnum(arith_t num)
5031{
5032 int len;
5033
5034 expdest = makestrspace(32, expdest);
5035#if ENABLE_ASH_MATH_SUPPORT_64
5036 len = fmtstr(expdest, 32, "%lld", (long long) num);
5037#else
5038 len = fmtstr(expdest, 32, "%ld", num);
5039#endif
5040 STADJUST(len, expdest);
5041 return len;
5042}
5043
5044static size_t
5045esclen(const char *start, const char *p)
5046{
5047 size_t esc = 0;
5048
5049 while (p > start && *--p == CTLESC) {
5050 esc++;
5051 }
5052 return esc;
5053}
5054
5055/*
5056 * Remove any CTLESC characters from a string.
5057 */
5058static char *
5059_rmescapes(char *str, int flag)
5060{
5061 char *p, *q, *r;
5062 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5063 unsigned inquotes;
5064 int notescaped;
5065 int globbing;
5066
5067 p = strpbrk(str, qchars);
5068 if (!p) {
5069 return str;
5070 }
5071 q = p;
5072 r = str;
5073 if (flag & RMESCAPE_ALLOC) {
5074 size_t len = p - str;
5075 size_t fulllen = len + strlen(p) + 1;
5076
5077 if (flag & RMESCAPE_GROW) {
5078 r = makestrspace(fulllen, expdest);
5079 } else if (flag & RMESCAPE_HEAP) {
5080 r = ckmalloc(fulllen);
5081 } else {
5082 r = stalloc(fulllen);
5083 }
5084 q = r;
5085 if (len > 0) {
5086 q = memcpy(q, str, len) + len;
5087 }
5088 }
5089 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5090 globbing = flag & RMESCAPE_GLOB;
5091 notescaped = globbing;
5092 while (*p) {
5093 if (*p == CTLQUOTEMARK) {
5094 inquotes = ~inquotes;
5095 p++;
5096 notescaped = globbing;
5097 continue;
5098 }
5099 if (*p == '\\') {
5100 /* naked back slash */
5101 notescaped = 0;
5102 goto copy;
5103 }
5104 if (*p == CTLESC) {
5105 p++;
5106 if (notescaped && inquotes && *p != '/') {
5107 *q++ = '\\';
5108 }
5109 }
5110 notescaped = globbing;
5111 copy:
5112 *q++ = *p++;
5113 }
5114 *q = '\0';
5115 if (flag & RMESCAPE_GROW) {
5116 expdest = r;
5117 STADJUST(q - r + 1, expdest);
5118 }
5119 return r;
5120}
5121#define rmescapes(p) _rmescapes((p), 0)
5122
5123#define pmatch(a, b) !fnmatch((a), (b), 0)
5124
5125/*
5126 * Prepare a pattern for a expmeta (internal glob(3)) call.
5127 *
5128 * Returns an stalloced string.
5129 */
5130static char *
5131preglob(const char *pattern, int quoted, int flag)
5132{
5133 flag |= RMESCAPE_GLOB;
5134 if (quoted) {
5135 flag |= RMESCAPE_QUOTED;
5136 }
5137 return _rmescapes((char *)pattern, flag);
5138}
5139
5140/*
5141 * Put a string on the stack.
5142 */
5143static void
5144memtodest(const char *p, size_t len, int syntax, int quotes)
5145{
5146 char *q = expdest;
5147
5148 q = makestrspace(len * 2, q);
5149
5150 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005151 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005152 if (!c)
5153 continue;
5154 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5155 USTPUTC(CTLESC, q);
5156 USTPUTC(c, q);
5157 }
5158
5159 expdest = q;
5160}
5161
5162static void
5163strtodest(const char *p, int syntax, int quotes)
5164{
5165 memtodest(p, strlen(p), syntax, quotes);
5166}
5167
5168/*
5169 * Record the fact that we have to scan this region of the
5170 * string for IFS characters.
5171 */
5172static void
5173recordregion(int start, int end, int nulonly)
5174{
5175 struct ifsregion *ifsp;
5176
5177 if (ifslastp == NULL) {
5178 ifsp = &ifsfirst;
5179 } else {
5180 INT_OFF;
5181 ifsp = ckmalloc(sizeof(*ifsp));
5182 ifsp->next = NULL;
5183 ifslastp->next = ifsp;
5184 INT_ON;
5185 }
5186 ifslastp = ifsp;
5187 ifslastp->begoff = start;
5188 ifslastp->endoff = end;
5189 ifslastp->nulonly = nulonly;
5190}
5191
5192static void
5193removerecordregions(int endoff)
5194{
5195 if (ifslastp == NULL)
5196 return;
5197
5198 if (ifsfirst.endoff > endoff) {
5199 while (ifsfirst.next != NULL) {
5200 struct ifsregion *ifsp;
5201 INT_OFF;
5202 ifsp = ifsfirst.next->next;
5203 free(ifsfirst.next);
5204 ifsfirst.next = ifsp;
5205 INT_ON;
5206 }
5207 if (ifsfirst.begoff > endoff)
5208 ifslastp = NULL;
5209 else {
5210 ifslastp = &ifsfirst;
5211 ifsfirst.endoff = endoff;
5212 }
5213 return;
5214 }
5215
5216 ifslastp = &ifsfirst;
5217 while (ifslastp->next && ifslastp->next->begoff < endoff)
5218 ifslastp=ifslastp->next;
5219 while (ifslastp->next != NULL) {
5220 struct ifsregion *ifsp;
5221 INT_OFF;
5222 ifsp = ifslastp->next->next;
5223 free(ifslastp->next);
5224 ifslastp->next = ifsp;
5225 INT_ON;
5226 }
5227 if (ifslastp->endoff > endoff)
5228 ifslastp->endoff = endoff;
5229}
5230
5231static char *
5232exptilde(char *startp, char *p, int flag)
5233{
5234 char c;
5235 char *name;
5236 struct passwd *pw;
5237 const char *home;
5238 int quotes = flag & (EXP_FULL | EXP_CASE);
5239 int startloc;
5240
5241 name = p + 1;
5242
5243 while ((c = *++p) != '\0') {
5244 switch (c) {
5245 case CTLESC:
5246 return startp;
5247 case CTLQUOTEMARK:
5248 return startp;
5249 case ':':
5250 if (flag & EXP_VARTILDE)
5251 goto done;
5252 break;
5253 case '/':
5254 case CTLENDVAR:
5255 goto done;
5256 }
5257 }
5258 done:
5259 *p = '\0';
5260 if (*name == '\0') {
5261 home = lookupvar(homestr);
5262 } else {
5263 pw = getpwnam(name);
5264 if (pw == NULL)
5265 goto lose;
5266 home = pw->pw_dir;
5267 }
5268 if (!home || !*home)
5269 goto lose;
5270 *p = c;
5271 startloc = expdest - (char *)stackblock();
5272 strtodest(home, SQSYNTAX, quotes);
5273 recordregion(startloc, expdest - (char *)stackblock(), 0);
5274 return p;
5275 lose:
5276 *p = c;
5277 return startp;
5278}
5279
5280/*
5281 * Execute a command inside back quotes. If it's a builtin command, we
5282 * want to save its output in a block obtained from malloc. Otherwise
5283 * we fork off a subprocess and get the output of the command via a pipe.
5284 * Should be called with interrupts off.
5285 */
5286struct backcmd { /* result of evalbackcmd */
5287 int fd; /* file descriptor to read from */
5288 char *buf; /* buffer */
5289 int nleft; /* number of chars in buffer */
5290 struct job *jp; /* job structure for command */
5291};
5292
5293/* These forward decls are needed to use "eval" code for backticks handling: */
5294static int back_exitstatus; /* exit status of backquoted command */
5295#define EV_EXIT 01 /* exit after evaluating tree */
5296static void evaltree(union node *, int);
5297
5298static void
5299evalbackcmd(union node *n, struct backcmd *result)
5300{
5301 int saveherefd;
5302
5303 result->fd = -1;
5304 result->buf = NULL;
5305 result->nleft = 0;
5306 result->jp = NULL;
5307 if (n == NULL) {
5308 goto out;
5309 }
5310
5311 saveherefd = herefd;
5312 herefd = -1;
5313
5314 {
5315 int pip[2];
5316 struct job *jp;
5317
5318 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005319 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005320 jp = makejob(n, 1);
5321 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5322 FORCE_INT_ON;
5323 close(pip[0]);
5324 if (pip[1] != 1) {
5325 close(1);
5326 copyfd(pip[1], 1);
5327 close(pip[1]);
5328 }
5329 eflag = 0;
5330 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5331 /* NOTREACHED */
5332 }
5333 close(pip[1]);
5334 result->fd = pip[0];
5335 result->jp = jp;
5336 }
5337 herefd = saveherefd;
5338 out:
5339 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5340 result->fd, result->buf, result->nleft, result->jp));
5341}
5342
5343/*
5344 * Expand stuff in backwards quotes.
5345 */
5346static void
5347expbackq(union node *cmd, int quoted, int quotes)
5348{
5349 struct backcmd in;
5350 int i;
5351 char buf[128];
5352 char *p;
5353 char *dest;
5354 int startloc;
5355 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5356 struct stackmark smark;
5357
5358 INT_OFF;
5359 setstackmark(&smark);
5360 dest = expdest;
5361 startloc = dest - (char *)stackblock();
5362 grabstackstr(dest);
5363 evalbackcmd(cmd, &in);
5364 popstackmark(&smark);
5365
5366 p = in.buf;
5367 i = in.nleft;
5368 if (i == 0)
5369 goto read;
5370 for (;;) {
5371 memtodest(p, i, syntax, quotes);
5372 read:
5373 if (in.fd < 0)
5374 break;
5375 i = safe_read(in.fd, buf, sizeof(buf));
5376 TRACE(("expbackq: read returns %d\n", i));
5377 if (i <= 0)
5378 break;
5379 p = buf;
5380 }
5381
5382 if (in.buf)
5383 free(in.buf);
5384 if (in.fd >= 0) {
5385 close(in.fd);
5386 back_exitstatus = waitforjob(in.jp);
5387 }
5388 INT_ON;
5389
5390 /* Eat all trailing newlines */
5391 dest = expdest;
5392 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5393 STUNPUTC(dest);
5394 expdest = dest;
5395
5396 if (quoted == 0)
5397 recordregion(startloc, dest - (char *)stackblock(), 0);
5398 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5399 (dest - (char *)stackblock()) - startloc,
5400 (dest - (char *)stackblock()) - startloc,
5401 stackblock() + startloc));
5402}
5403
5404#if ENABLE_ASH_MATH_SUPPORT
5405/*
5406 * Expand arithmetic expression. Backup to start of expression,
5407 * evaluate, place result in (backed up) result, adjust string position.
5408 */
5409static void
5410expari(int quotes)
5411{
5412 char *p, *start;
5413 int begoff;
5414 int flag;
5415 int len;
5416
5417 /* ifsfree(); */
5418
5419 /*
5420 * This routine is slightly over-complicated for
5421 * efficiency. Next we scan backwards looking for the
5422 * start of arithmetic.
5423 */
5424 start = stackblock();
5425 p = expdest - 1;
5426 *p = '\0';
5427 p--;
5428 do {
5429 int esc;
5430
5431 while (*p != CTLARI) {
5432 p--;
5433#if DEBUG
5434 if (p < start) {
5435 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5436 }
5437#endif
5438 }
5439
5440 esc = esclen(start, p);
5441 if (!(esc % 2)) {
5442 break;
5443 }
5444
5445 p -= esc + 1;
5446 } while (1);
5447
5448 begoff = p - start;
5449
5450 removerecordregions(begoff);
5451
5452 flag = p[1];
5453
5454 expdest = p;
5455
5456 if (quotes)
5457 rmescapes(p + 2);
5458
5459 len = cvtnum(dash_arith(p + 2));
5460
5461 if (flag != '"')
5462 recordregion(begoff, begoff + len, 0);
5463}
5464#endif
5465
5466/* argstr needs it */
5467static char *evalvar(char *p, int flag);
5468
5469/*
5470 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5471 * characters to allow for further processing. Otherwise treat
5472 * $@ like $* since no splitting will be performed.
5473 */
5474static void
5475argstr(char *p, int flag)
5476{
5477 static const char spclchars[] = {
5478 '=',
5479 ':',
5480 CTLQUOTEMARK,
5481 CTLENDVAR,
5482 CTLESC,
5483 CTLVAR,
5484 CTLBACKQ,
5485 CTLBACKQ | CTLQUOTE,
5486#if ENABLE_ASH_MATH_SUPPORT
5487 CTLENDARI,
5488#endif
5489 0
5490 };
5491 const char *reject = spclchars;
5492 int c;
5493 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5494 int breakall = flag & EXP_WORD;
5495 int inquotes;
5496 size_t length;
5497 int startloc;
5498
5499 if (!(flag & EXP_VARTILDE)) {
5500 reject += 2;
5501 } else if (flag & EXP_VARTILDE2) {
5502 reject++;
5503 }
5504 inquotes = 0;
5505 length = 0;
5506 if (flag & EXP_TILDE) {
5507 char *q;
5508
5509 flag &= ~EXP_TILDE;
5510 tilde:
5511 q = p;
5512 if (*q == CTLESC && (flag & EXP_QWORD))
5513 q++;
5514 if (*q == '~')
5515 p = exptilde(p, q, flag);
5516 }
5517 start:
5518 startloc = expdest - (char *)stackblock();
5519 for (;;) {
5520 length += strcspn(p + length, reject);
5521 c = p[length];
5522 if (c && (!(c & 0x80)
5523#if ENABLE_ASH_MATH_SUPPORT
5524 || c == CTLENDARI
5525#endif
5526 )) {
5527 /* c == '=' || c == ':' || c == CTLENDARI */
5528 length++;
5529 }
5530 if (length > 0) {
5531 int newloc;
5532 expdest = stack_nputstr(p, length, expdest);
5533 newloc = expdest - (char *)stackblock();
5534 if (breakall && !inquotes && newloc > startloc) {
5535 recordregion(startloc, newloc, 0);
5536 }
5537 startloc = newloc;
5538 }
5539 p += length + 1;
5540 length = 0;
5541
5542 switch (c) {
5543 case '\0':
5544 goto breakloop;
5545 case '=':
5546 if (flag & EXP_VARTILDE2) {
5547 p--;
5548 continue;
5549 }
5550 flag |= EXP_VARTILDE2;
5551 reject++;
5552 /* fall through */
5553 case ':':
5554 /*
5555 * sort of a hack - expand tildes in variable
5556 * assignments (after the first '=' and after ':'s).
5557 */
5558 if (*--p == '~') {
5559 goto tilde;
5560 }
5561 continue;
5562 }
5563
5564 switch (c) {
5565 case CTLENDVAR: /* ??? */
5566 goto breakloop;
5567 case CTLQUOTEMARK:
5568 /* "$@" syntax adherence hack */
5569 if (
5570 !inquotes &&
5571 !memcmp(p, dolatstr, 4) &&
5572 (p[4] == CTLQUOTEMARK || (
5573 p[4] == CTLENDVAR &&
5574 p[5] == CTLQUOTEMARK
5575 ))
5576 ) {
5577 p = evalvar(p + 1, flag) + 1;
5578 goto start;
5579 }
5580 inquotes = !inquotes;
5581 addquote:
5582 if (quotes) {
5583 p--;
5584 length++;
5585 startloc++;
5586 }
5587 break;
5588 case CTLESC:
5589 startloc++;
5590 length++;
5591 goto addquote;
5592 case CTLVAR:
5593 p = evalvar(p, flag);
5594 goto start;
5595 case CTLBACKQ:
5596 c = 0;
5597 case CTLBACKQ|CTLQUOTE:
5598 expbackq(argbackq->n, c, quotes);
5599 argbackq = argbackq->next;
5600 goto start;
5601#if ENABLE_ASH_MATH_SUPPORT
5602 case CTLENDARI:
5603 p--;
5604 expari(quotes);
5605 goto start;
5606#endif
5607 }
5608 }
5609 breakloop:
5610 ;
5611}
5612
5613static char *
5614scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5615 int zero)
5616{
5617 char *loc;
5618 char *loc2;
5619 char c;
5620
5621 loc = startp;
5622 loc2 = rmesc;
5623 do {
5624 int match;
5625 const char *s = loc2;
5626 c = *loc2;
5627 if (zero) {
5628 *loc2 = '\0';
5629 s = rmesc;
5630 }
5631 match = pmatch(str, s);
5632 *loc2 = c;
5633 if (match)
5634 return loc;
5635 if (quotes && *loc == CTLESC)
5636 loc++;
5637 loc++;
5638 loc2++;
5639 } while (c);
5640 return 0;
5641}
5642
5643static char *
5644scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5645 int zero)
5646{
5647 int esc = 0;
5648 char *loc;
5649 char *loc2;
5650
5651 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5652 int match;
5653 char c = *loc2;
5654 const char *s = loc2;
5655 if (zero) {
5656 *loc2 = '\0';
5657 s = rmesc;
5658 }
5659 match = pmatch(str, s);
5660 *loc2 = c;
5661 if (match)
5662 return loc;
5663 loc--;
5664 if (quotes) {
5665 if (--esc < 0) {
5666 esc = esclen(startp, loc);
5667 }
5668 if (esc % 2) {
5669 esc--;
5670 loc--;
5671 }
5672 }
5673 }
5674 return 0;
5675}
5676
5677static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5678static void
5679varunset(const char *end, const char *var, const char *umsg, int varflags)
5680{
5681 const char *msg;
5682 const char *tail;
5683
5684 tail = nullstr;
5685 msg = "parameter not set";
5686 if (umsg) {
5687 if (*end == CTLENDVAR) {
5688 if (varflags & VSNUL)
5689 tail = " or null";
5690 } else
5691 msg = umsg;
5692 }
5693 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5694}
5695
5696static const char *
5697subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5698{
5699 char *startp;
5700 char *loc;
5701 int saveherefd = herefd;
5702 struct nodelist *saveargbackq = argbackq;
5703 int amount;
5704 char *rmesc, *rmescend;
5705 int zero;
5706 char *(*scan)(char *, char *, char *, char *, int , int);
5707
5708 herefd = -1;
5709 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5710 STPUTC('\0', expdest);
5711 herefd = saveherefd;
5712 argbackq = saveargbackq;
5713 startp = stackblock() + startloc;
5714
5715 switch (subtype) {
5716 case VSASSIGN:
5717 setvar(str, startp, 0);
5718 amount = startp - expdest;
5719 STADJUST(amount, expdest);
5720 return startp;
5721
5722 case VSQUESTION:
5723 varunset(p, str, startp, varflags);
5724 /* NOTREACHED */
5725 }
5726
5727 subtype -= VSTRIMRIGHT;
5728#if DEBUG
5729 if (subtype < 0 || subtype > 3)
5730 abort();
5731#endif
5732
5733 rmesc = startp;
5734 rmescend = stackblock() + strloc;
5735 if (quotes) {
5736 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5737 if (rmesc != startp) {
5738 rmescend = expdest;
5739 startp = stackblock() + startloc;
5740 }
5741 }
5742 rmescend--;
5743 str = stackblock() + strloc;
5744 preglob(str, varflags & VSQUOTE, 0);
5745
5746 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5747 zero = subtype >> 1;
5748 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5749 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5750
5751 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5752 if (loc) {
5753 if (zero) {
5754 memmove(startp, loc, str - loc);
5755 loc = startp + (str - loc) - 1;
5756 }
5757 *loc = '\0';
5758 amount = loc - expdest;
5759 STADJUST(amount, expdest);
5760 }
5761 return loc;
5762}
5763
5764/*
5765 * Add the value of a specialized variable to the stack string.
5766 */
5767static ssize_t
5768varvalue(char *name, int varflags, int flags)
5769{
5770 int num;
5771 char *p;
5772 int i;
5773 int sep = 0;
5774 int sepq = 0;
5775 ssize_t len = 0;
5776 char **ap;
5777 int syntax;
5778 int quoted = varflags & VSQUOTE;
5779 int subtype = varflags & VSTYPE;
5780 int quotes = flags & (EXP_FULL | EXP_CASE);
5781
5782 if (quoted && (flags & EXP_FULL))
5783 sep = 1 << CHAR_BIT;
5784
5785 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5786 switch (*name) {
5787 case '$':
5788 num = rootpid;
5789 goto numvar;
5790 case '?':
5791 num = exitstatus;
5792 goto numvar;
5793 case '#':
5794 num = shellparam.nparam;
5795 goto numvar;
5796 case '!':
5797 num = backgndpid;
5798 if (num == 0)
5799 return -1;
5800 numvar:
5801 len = cvtnum(num);
5802 break;
5803 case '-':
5804 p = makestrspace(NOPTS, expdest);
5805 for (i = NOPTS - 1; i >= 0; i--) {
5806 if (optlist[i]) {
5807 USTPUTC(optletters(i), p);
5808 len++;
5809 }
5810 }
5811 expdest = p;
5812 break;
5813 case '@':
5814 if (sep)
5815 goto param;
5816 /* fall through */
5817 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005818 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005819 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5820 sepq = 1;
5821 param:
5822 ap = shellparam.p;
5823 if (!ap)
5824 return -1;
5825 while ((p = *ap++)) {
5826 size_t partlen;
5827
5828 partlen = strlen(p);
5829 len += partlen;
5830
5831 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5832 memtodest(p, partlen, syntax, quotes);
5833
5834 if (*ap && sep) {
5835 char *q;
5836
5837 len++;
5838 if (subtype == VSPLUS || subtype == VSLENGTH) {
5839 continue;
5840 }
5841 q = expdest;
5842 if (sepq)
5843 STPUTC(CTLESC, q);
5844 STPUTC(sep, q);
5845 expdest = q;
5846 }
5847 }
5848 return len;
5849 case '0':
5850 case '1':
5851 case '2':
5852 case '3':
5853 case '4':
5854 case '5':
5855 case '6':
5856 case '7':
5857 case '8':
5858 case '9':
5859 num = atoi(name);
5860 if (num < 0 || num > shellparam.nparam)
5861 return -1;
5862 p = num ? shellparam.p[num - 1] : arg0;
5863 goto value;
5864 default:
5865 p = lookupvar(name);
5866 value:
5867 if (!p)
5868 return -1;
5869
5870 len = strlen(p);
5871 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5872 memtodest(p, len, syntax, quotes);
5873 return len;
5874 }
5875
5876 if (subtype == VSPLUS || subtype == VSLENGTH)
5877 STADJUST(-len, expdest);
5878 return len;
5879}
5880
5881/*
5882 * Expand a variable, and return a pointer to the next character in the
5883 * input string.
5884 */
5885static char *
5886evalvar(char *p, int flag)
5887{
5888 int subtype;
5889 int varflags;
5890 char *var;
5891 int patloc;
5892 int c;
5893 int startloc;
5894 ssize_t varlen;
5895 int easy;
5896 int quotes;
5897 int quoted;
5898
5899 quotes = flag & (EXP_FULL | EXP_CASE);
5900 varflags = *p++;
5901 subtype = varflags & VSTYPE;
5902 quoted = varflags & VSQUOTE;
5903 var = p;
5904 easy = (!quoted || (*var == '@' && shellparam.nparam));
5905 startloc = expdest - (char *)stackblock();
5906 p = strchr(p, '=') + 1;
5907
5908 again:
5909 varlen = varvalue(var, varflags, flag);
5910 if (varflags & VSNUL)
5911 varlen--;
5912
5913 if (subtype == VSPLUS) {
5914 varlen = -1 - varlen;
5915 goto vsplus;
5916 }
5917
5918 if (subtype == VSMINUS) {
5919 vsplus:
5920 if (varlen < 0) {
5921 argstr(
5922 p, flag | EXP_TILDE |
5923 (quoted ? EXP_QWORD : EXP_WORD)
5924 );
5925 goto end;
5926 }
5927 if (easy)
5928 goto record;
5929 goto end;
5930 }
5931
5932 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5933 if (varlen < 0) {
5934 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5935 varflags &= ~VSNUL;
5936 /*
5937 * Remove any recorded regions beyond
5938 * start of variable
5939 */
5940 removerecordregions(startloc);
5941 goto again;
5942 }
5943 goto end;
5944 }
5945 if (easy)
5946 goto record;
5947 goto end;
5948 }
5949
5950 if (varlen < 0 && uflag)
5951 varunset(p, var, 0, 0);
5952
5953 if (subtype == VSLENGTH) {
5954 cvtnum(varlen > 0 ? varlen : 0);
5955 goto record;
5956 }
5957
5958 if (subtype == VSNORMAL) {
5959 if (!easy)
5960 goto end;
5961 record:
5962 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5963 goto end;
5964 }
5965
5966#if DEBUG
5967 switch (subtype) {
5968 case VSTRIMLEFT:
5969 case VSTRIMLEFTMAX:
5970 case VSTRIMRIGHT:
5971 case VSTRIMRIGHTMAX:
5972 break;
5973 default:
5974 abort();
5975 }
5976#endif
5977
5978 if (varlen >= 0) {
5979 /*
5980 * Terminate the string and start recording the pattern
5981 * right after it
5982 */
5983 STPUTC('\0', expdest);
5984 patloc = expdest - (char *)stackblock();
5985 if (subevalvar(p, NULL, patloc, subtype,
5986 startloc, varflags, quotes) == 0) {
5987 int amount = expdest - (
5988 (char *)stackblock() + patloc - 1
5989 );
5990 STADJUST(-amount, expdest);
5991 }
5992 /* Remove any recorded regions beyond start of variable */
5993 removerecordregions(startloc);
5994 goto record;
5995 }
5996
5997 end:
5998 if (subtype != VSNORMAL) { /* skip to end of alternative */
5999 int nesting = 1;
6000 for (;;) {
6001 c = *p++;
6002 if (c == CTLESC)
6003 p++;
6004 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6005 if (varlen >= 0)
6006 argbackq = argbackq->next;
6007 } else if (c == CTLVAR) {
6008 if ((*p++ & VSTYPE) != VSNORMAL)
6009 nesting++;
6010 } else if (c == CTLENDVAR) {
6011 if (--nesting == 0)
6012 break;
6013 }
6014 }
6015 }
6016 return p;
6017}
6018
6019/*
6020 * Break the argument string into pieces based upon IFS and add the
6021 * strings to the argument list. The regions of the string to be
6022 * searched for IFS characters have been stored by recordregion.
6023 */
6024static void
6025ifsbreakup(char *string, struct arglist *arglist)
6026{
6027 struct ifsregion *ifsp;
6028 struct strlist *sp;
6029 char *start;
6030 char *p;
6031 char *q;
6032 const char *ifs, *realifs;
6033 int ifsspc;
6034 int nulonly;
6035
6036 start = string;
6037 if (ifslastp != NULL) {
6038 ifsspc = 0;
6039 nulonly = 0;
6040 realifs = ifsset() ? ifsval() : defifs;
6041 ifsp = &ifsfirst;
6042 do {
6043 p = string + ifsp->begoff;
6044 nulonly = ifsp->nulonly;
6045 ifs = nulonly ? nullstr : realifs;
6046 ifsspc = 0;
6047 while (p < string + ifsp->endoff) {
6048 q = p;
6049 if (*p == CTLESC)
6050 p++;
6051 if (!strchr(ifs, *p)) {
6052 p++;
6053 continue;
6054 }
6055 if (!nulonly)
6056 ifsspc = (strchr(defifs, *p) != NULL);
6057 /* Ignore IFS whitespace at start */
6058 if (q == start && ifsspc) {
6059 p++;
6060 start = p;
6061 continue;
6062 }
6063 *q = '\0';
6064 sp = stalloc(sizeof(*sp));
6065 sp->text = start;
6066 *arglist->lastp = sp;
6067 arglist->lastp = &sp->next;
6068 p++;
6069 if (!nulonly) {
6070 for (;;) {
6071 if (p >= string + ifsp->endoff) {
6072 break;
6073 }
6074 q = p;
6075 if (*p == CTLESC)
6076 p++;
6077 if (strchr(ifs, *p) == NULL ) {
6078 p = q;
6079 break;
6080 } else if (strchr(defifs, *p) == NULL) {
6081 if (ifsspc) {
6082 p++;
6083 ifsspc = 0;
6084 } else {
6085 p = q;
6086 break;
6087 }
6088 } else
6089 p++;
6090 }
6091 }
6092 start = p;
6093 } /* while */
6094 ifsp = ifsp->next;
6095 } while (ifsp != NULL);
6096 if (nulonly)
6097 goto add;
6098 }
6099
6100 if (!*start)
6101 return;
6102
6103 add:
6104 sp = stalloc(sizeof(*sp));
6105 sp->text = start;
6106 *arglist->lastp = sp;
6107 arglist->lastp = &sp->next;
6108}
6109
6110static void
6111ifsfree(void)
6112{
6113 struct ifsregion *p;
6114
6115 INT_OFF;
6116 p = ifsfirst.next;
6117 do {
6118 struct ifsregion *ifsp;
6119 ifsp = p->next;
6120 free(p);
6121 p = ifsp;
6122 } while (p);
6123 ifslastp = NULL;
6124 ifsfirst.next = NULL;
6125 INT_ON;
6126}
6127
6128/*
6129 * Add a file name to the list.
6130 */
6131static void
6132addfname(const char *name)
6133{
6134 struct strlist *sp;
6135
6136 sp = stalloc(sizeof(*sp));
6137 sp->text = ststrdup(name);
6138 *exparg.lastp = sp;
6139 exparg.lastp = &sp->next;
6140}
6141
6142static char *expdir;
6143
6144/*
6145 * Do metacharacter (i.e. *, ?, [...]) expansion.
6146 */
6147static void
6148expmeta(char *enddir, char *name)
6149{
6150 char *p;
6151 const char *cp;
6152 char *start;
6153 char *endname;
6154 int metaflag;
6155 struct stat statb;
6156 DIR *dirp;
6157 struct dirent *dp;
6158 int atend;
6159 int matchdot;
6160
6161 metaflag = 0;
6162 start = name;
6163 for (p = name; *p; p++) {
6164 if (*p == '*' || *p == '?')
6165 metaflag = 1;
6166 else if (*p == '[') {
6167 char *q = p + 1;
6168 if (*q == '!')
6169 q++;
6170 for (;;) {
6171 if (*q == '\\')
6172 q++;
6173 if (*q == '/' || *q == '\0')
6174 break;
6175 if (*++q == ']') {
6176 metaflag = 1;
6177 break;
6178 }
6179 }
6180 } else if (*p == '\\')
6181 p++;
6182 else if (*p == '/') {
6183 if (metaflag)
6184 goto out;
6185 start = p + 1;
6186 }
6187 }
6188 out:
6189 if (metaflag == 0) { /* we've reached the end of the file name */
6190 if (enddir != expdir)
6191 metaflag++;
6192 p = name;
6193 do {
6194 if (*p == '\\')
6195 p++;
6196 *enddir++ = *p;
6197 } while (*p++);
6198 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6199 addfname(expdir);
6200 return;
6201 }
6202 endname = p;
6203 if (name < start) {
6204 p = name;
6205 do {
6206 if (*p == '\\')
6207 p++;
6208 *enddir++ = *p++;
6209 } while (p < start);
6210 }
6211 if (enddir == expdir) {
6212 cp = ".";
6213 } else if (enddir == expdir + 1 && *expdir == '/') {
6214 cp = "/";
6215 } else {
6216 cp = expdir;
6217 enddir[-1] = '\0';
6218 }
6219 dirp = opendir(cp);
6220 if (dirp == NULL)
6221 return;
6222 if (enddir != expdir)
6223 enddir[-1] = '/';
6224 if (*endname == 0) {
6225 atend = 1;
6226 } else {
6227 atend = 0;
6228 *endname++ = '\0';
6229 }
6230 matchdot = 0;
6231 p = start;
6232 if (*p == '\\')
6233 p++;
6234 if (*p == '.')
6235 matchdot++;
6236 while (! intpending && (dp = readdir(dirp)) != NULL) {
6237 if (dp->d_name[0] == '.' && ! matchdot)
6238 continue;
6239 if (pmatch(start, dp->d_name)) {
6240 if (atend) {
6241 strcpy(enddir, dp->d_name);
6242 addfname(expdir);
6243 } else {
6244 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6245 continue;
6246 p[-1] = '/';
6247 expmeta(p, endname);
6248 }
6249 }
6250 }
6251 closedir(dirp);
6252 if (! atend)
6253 endname[-1] = '/';
6254}
6255
6256static struct strlist *
6257msort(struct strlist *list, int len)
6258{
6259 struct strlist *p, *q = NULL;
6260 struct strlist **lpp;
6261 int half;
6262 int n;
6263
6264 if (len <= 1)
6265 return list;
6266 half = len >> 1;
6267 p = list;
6268 for (n = half; --n >= 0; ) {
6269 q = p;
6270 p = p->next;
6271 }
6272 q->next = NULL; /* terminate first half of list */
6273 q = msort(list, half); /* sort first half of list */
6274 p = msort(p, len - half); /* sort second half */
6275 lpp = &list;
6276 for (;;) {
6277#if ENABLE_LOCALE_SUPPORT
6278 if (strcoll(p->text, q->text) < 0)
6279#else
6280 if (strcmp(p->text, q->text) < 0)
6281#endif
6282 {
6283 *lpp = p;
6284 lpp = &p->next;
6285 p = *lpp;
6286 if (p == NULL) {
6287 *lpp = q;
6288 break;
6289 }
6290 } else {
6291 *lpp = q;
6292 lpp = &q->next;
6293 q = *lpp;
6294 if (q == NULL) {
6295 *lpp = p;
6296 break;
6297 }
6298 }
6299 }
6300 return list;
6301}
6302
6303/*
6304 * Sort the results of file name expansion. It calculates the number of
6305 * strings to sort and then calls msort (short for merge sort) to do the
6306 * work.
6307 */
6308static struct strlist *
6309expsort(struct strlist *str)
6310{
6311 int len;
6312 struct strlist *sp;
6313
6314 len = 0;
6315 for (sp = str; sp; sp = sp->next)
6316 len++;
6317 return msort(str, len);
6318}
6319
6320static void
6321expandmeta(struct strlist *str, int flag)
6322{
6323 static const char metachars[] = {
6324 '*', '?', '[', 0
6325 };
6326 /* TODO - EXP_REDIR */
6327
6328 while (str) {
6329 struct strlist **savelastp;
6330 struct strlist *sp;
6331 char *p;
6332
6333 if (fflag)
6334 goto nometa;
6335 if (!strpbrk(str->text, metachars))
6336 goto nometa;
6337 savelastp = exparg.lastp;
6338
6339 INT_OFF;
6340 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6341 {
6342 int i = strlen(str->text);
6343 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6344 }
6345
6346 expmeta(expdir, p);
6347 free(expdir);
6348 if (p != str->text)
6349 free(p);
6350 INT_ON;
6351 if (exparg.lastp == savelastp) {
6352 /*
6353 * no matches
6354 */
6355 nometa:
6356 *exparg.lastp = str;
6357 rmescapes(str->text);
6358 exparg.lastp = &str->next;
6359 } else {
6360 *exparg.lastp = NULL;
6361 *savelastp = sp = expsort(*savelastp);
6362 while (sp->next != NULL)
6363 sp = sp->next;
6364 exparg.lastp = &sp->next;
6365 }
6366 str = str->next;
6367 }
6368}
6369
6370/*
6371 * Perform variable substitution and command substitution on an argument,
6372 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6373 * perform splitting and file name expansion. When arglist is NULL, perform
6374 * here document expansion.
6375 */
6376static void
6377expandarg(union node *arg, struct arglist *arglist, int flag)
6378{
6379 struct strlist *sp;
6380 char *p;
6381
6382 argbackq = arg->narg.backquote;
6383 STARTSTACKSTR(expdest);
6384 ifsfirst.next = NULL;
6385 ifslastp = NULL;
6386 argstr(arg->narg.text, flag);
6387 p = _STPUTC('\0', expdest);
6388 expdest = p - 1;
6389 if (arglist == NULL) {
6390 return; /* here document expanded */
6391 }
6392 p = grabstackstr(p);
6393 exparg.lastp = &exparg.list;
6394 /*
6395 * TODO - EXP_REDIR
6396 */
6397 if (flag & EXP_FULL) {
6398 ifsbreakup(p, &exparg);
6399 *exparg.lastp = NULL;
6400 exparg.lastp = &exparg.list;
6401 expandmeta(exparg.list, flag);
6402 } else {
6403 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6404 rmescapes(p);
6405 sp = stalloc(sizeof(*sp));
6406 sp->text = p;
6407 *exparg.lastp = sp;
6408 exparg.lastp = &sp->next;
6409 }
6410 if (ifsfirst.next)
6411 ifsfree();
6412 *exparg.lastp = NULL;
6413 if (exparg.list) {
6414 *arglist->lastp = exparg.list;
6415 arglist->lastp = exparg.lastp;
6416 }
6417}
6418
6419/*
6420 * Expand shell variables and backquotes inside a here document.
6421 */
6422static void
6423expandhere(union node *arg, int fd)
6424{
6425 herefd = fd;
6426 expandarg(arg, (struct arglist *)NULL, 0);
6427 full_write(fd, stackblock(), expdest - (char *)stackblock());
6428}
6429
6430/*
6431 * Returns true if the pattern matches the string.
6432 */
6433static int
6434patmatch(char *pattern, const char *string)
6435{
6436 return pmatch(preglob(pattern, 0, 0), string);
6437}
6438
6439/*
6440 * See if a pattern matches in a case statement.
6441 */
6442static int
6443casematch(union node *pattern, char *val)
6444{
6445 struct stackmark smark;
6446 int result;
6447
6448 setstackmark(&smark);
6449 argbackq = pattern->narg.backquote;
6450 STARTSTACKSTR(expdest);
6451 ifslastp = NULL;
6452 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6453 STACKSTRNUL(expdest);
6454 result = patmatch(stackblock(), val);
6455 popstackmark(&smark);
6456 return result;
6457}
6458
6459
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006460/* ============ find_command */
6461
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006462#if ENABLE_FEATURE_SH_STANDALONE_SHELL
6463static int
6464is_safe_applet(char *name)
Denis Vlasenko99eb8502007-02-23 21:09:49 +00006465{
6466 /* It isn't a bug to have non-existent applet here... */
6467 /* ...just a waste of space... */
6468 static const char safe_applets[][8] = {
6469 "["
6470 USE_AWK (, "awk" )
6471 USE_CAT (, "cat" )
6472 USE_CHMOD (, "chmod" )
6473 USE_CHOWN (, "chown" )
6474 USE_CP (, "cp" )
6475 USE_CUT (, "cut" )
6476 USE_DD (, "dd" )
6477 USE_ECHO (, "echo" )
6478 USE_FIND (, "find" )
6479 USE_HEXDUMP(, "hexdump")
6480 USE_LN (, "ln" )
6481 USE_LS (, "ls" )
6482 USE_MKDIR (, "mkdir" )
6483 USE_RM (, "rm" )
6484 USE_SORT (, "sort" )
6485 USE_TEST (, "test" )
6486 USE_TOUCH (, "touch" )
6487 USE_XARGS (, "xargs" )
6488 };
6489 int n = sizeof(safe_applets) / sizeof(safe_applets[0]);
6490 int i;
6491 for (i = 0; i < n; i++)
6492 if (strcmp(safe_applets[i], name) == 0)
6493 return 1;
6494
6495 return 0;
6496}
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006497#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +00006498
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006499struct builtincmd {
6500 const char *name;
6501 int (*builtin)(int, char **);
6502 /* unsigned flags; */
6503};
6504#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6505#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6506#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6507
6508struct cmdentry {
6509 int cmdtype;
6510 union param {
6511 int index;
6512 const struct builtincmd *cmd;
6513 struct funcnode *func;
6514 } u;
6515};
6516/* values of cmdtype */
6517#define CMDUNKNOWN -1 /* no entry in table for command */
6518#define CMDNORMAL 0 /* command is an executable program */
6519#define CMDFUNCTION 1 /* command is a shell function */
6520#define CMDBUILTIN 2 /* command is a shell builtin */
6521
6522/* action to find_command() */
6523#define DO_ERR 0x01 /* prints errors */
6524#define DO_ABS 0x02 /* checks absolute paths */
6525#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6526#define DO_ALTPATH 0x08 /* using alternate path */
6527#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6528
6529static void find_command(char *, struct cmdentry *, int, const char *);
6530
6531
6532/* ============ Hashing commands */
6533
6534/*
6535 * When commands are first encountered, they are entered in a hash table.
6536 * This ensures that a full path search will not have to be done for them
6537 * on each invocation.
6538 *
6539 * We should investigate converting to a linear search, even though that
6540 * would make the command name "hash" a misnomer.
6541 */
6542
6543#define CMDTABLESIZE 31 /* should be prime */
6544#define ARB 1 /* actual size determined at run time */
6545
6546struct tblentry {
6547 struct tblentry *next; /* next entry in hash chain */
6548 union param param; /* definition of builtin function */
6549 short cmdtype; /* index identifying command */
6550 char rehash; /* if set, cd done since entry created */
6551 char cmdname[ARB]; /* name of command */
6552};
6553
6554static struct tblentry *cmdtable[CMDTABLESIZE];
6555static int builtinloc = -1; /* index in path of %builtin, or -1 */
6556
6557static void
6558tryexec(char *cmd, char **argv, char **envp)
6559{
6560 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006561
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006562#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006563 if (strchr(cmd, '/') == NULL) {
6564 struct BB_applet *a;
6565 char **c;
6566
6567 a = find_applet_by_name(cmd);
6568 if (a) {
6569 if (is_safe_applet(cmd)) {
6570 c = argv;
6571 while (*c)
6572 c++;
6573 applet_name = cmd;
6574 exit(a->main(c - argv, argv));
6575 }
6576 /* re-exec ourselves with the new arguments */
6577 execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
6578 /* If they called chroot or otherwise made the binary no longer
6579 * executable, fall through */
6580 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006581 }
6582#endif
6583
6584 repeat:
6585#ifdef SYSV
6586 do {
6587 execve(cmd, argv, envp);
6588 } while (errno == EINTR);
6589#else
6590 execve(cmd, argv, envp);
6591#endif
6592 if (repeated++) {
6593 free(argv);
6594 } else if (errno == ENOEXEC) {
6595 char **ap;
6596 char **new;
6597
6598 for (ap = argv; *ap; ap++)
6599 ;
6600 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6601 ap[1] = cmd;
6602 *ap = cmd = (char *)DEFAULT_SHELL;
6603 ap += 2;
6604 argv++;
6605 while ((*ap++ = *argv++))
6606 ;
6607 argv = new;
6608 goto repeat;
6609 }
6610}
6611
6612/*
6613 * Exec a program. Never returns. If you change this routine, you may
6614 * have to change the find_command routine as well.
6615 */
6616#define environment() listvars(VEXPORT, VUNSET, 0)
6617static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6618static void
6619shellexec(char **argv, const char *path, int idx)
6620{
6621 char *cmdname;
6622 int e;
6623 char **envp;
6624 int exerrno;
6625
6626 clearredir(1);
6627 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006628 if (strchr(argv[0], '/')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006629#if ENABLE_FEATURE_SH_STANDALONE_SHELL
6630 || find_applet_by_name(argv[0])
6631#endif
6632 ) {
6633 tryexec(argv[0], argv, envp);
6634 e = errno;
6635 } else {
6636 e = ENOENT;
6637 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6638 if (--idx < 0 && pathopt == NULL) {
6639 tryexec(cmdname, argv, envp);
6640 if (errno != ENOENT && errno != ENOTDIR)
6641 e = errno;
6642 }
6643 stunalloc(cmdname);
6644 }
6645 }
6646
6647 /* Map to POSIX errors */
6648 switch (e) {
6649 case EACCES:
6650 exerrno = 126;
6651 break;
6652 case ENOENT:
6653 exerrno = 127;
6654 break;
6655 default:
6656 exerrno = 2;
6657 break;
6658 }
6659 exitstatus = exerrno;
6660 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6661 argv[0], e, suppressint ));
6662 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6663 /* NOTREACHED */
6664}
6665
6666static void
6667printentry(struct tblentry *cmdp)
6668{
6669 int idx;
6670 const char *path;
6671 char *name;
6672
6673 idx = cmdp->param.index;
6674 path = pathval();
6675 do {
6676 name = padvance(&path, cmdp->cmdname);
6677 stunalloc(name);
6678 } while (--idx >= 0);
6679 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6680}
6681
6682/*
6683 * Clear out command entries. The argument specifies the first entry in
6684 * PATH which has changed.
6685 */
6686static void
6687clearcmdentry(int firstchange)
6688{
6689 struct tblentry **tblp;
6690 struct tblentry **pp;
6691 struct tblentry *cmdp;
6692
6693 INT_OFF;
6694 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6695 pp = tblp;
6696 while ((cmdp = *pp) != NULL) {
6697 if ((cmdp->cmdtype == CMDNORMAL &&
6698 cmdp->param.index >= firstchange)
6699 || (cmdp->cmdtype == CMDBUILTIN &&
6700 builtinloc >= firstchange)
6701 ) {
6702 *pp = cmdp->next;
6703 free(cmdp);
6704 } else {
6705 pp = &cmdp->next;
6706 }
6707 }
6708 }
6709 INT_ON;
6710}
6711
6712/*
6713 * Locate a command in the command hash table. If "add" is nonzero,
6714 * add the command to the table if it is not already present. The
6715 * variable "lastcmdentry" is set to point to the address of the link
6716 * pointing to the entry, so that delete_cmd_entry can delete the
6717 * entry.
6718 *
6719 * Interrupts must be off if called with add != 0.
6720 */
6721static struct tblentry **lastcmdentry;
6722
6723static struct tblentry *
6724cmdlookup(const char *name, int add)
6725{
6726 unsigned int hashval;
6727 const char *p;
6728 struct tblentry *cmdp;
6729 struct tblentry **pp;
6730
6731 p = name;
6732 hashval = (unsigned char)*p << 4;
6733 while (*p)
6734 hashval += (unsigned char)*p++;
6735 hashval &= 0x7FFF;
6736 pp = &cmdtable[hashval % CMDTABLESIZE];
6737 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6738 if (strcmp(cmdp->cmdname, name) == 0)
6739 break;
6740 pp = &cmdp->next;
6741 }
6742 if (add && cmdp == NULL) {
6743 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6744 + strlen(name) + 1);
6745 cmdp->next = NULL;
6746 cmdp->cmdtype = CMDUNKNOWN;
6747 strcpy(cmdp->cmdname, name);
6748 }
6749 lastcmdentry = pp;
6750 return cmdp;
6751}
6752
6753/*
6754 * Delete the command entry returned on the last lookup.
6755 */
6756static void
6757delete_cmd_entry(void)
6758{
6759 struct tblentry *cmdp;
6760
6761 INT_OFF;
6762 cmdp = *lastcmdentry;
6763 *lastcmdentry = cmdp->next;
6764 if (cmdp->cmdtype == CMDFUNCTION)
6765 freefunc(cmdp->param.func);
6766 free(cmdp);
6767 INT_ON;
6768}
6769
6770/*
6771 * Add a new command entry, replacing any existing command entry for
6772 * the same name - except special builtins.
6773 */
6774static void
6775addcmdentry(char *name, struct cmdentry *entry)
6776{
6777 struct tblentry *cmdp;
6778
6779 cmdp = cmdlookup(name, 1);
6780 if (cmdp->cmdtype == CMDFUNCTION) {
6781 freefunc(cmdp->param.func);
6782 }
6783 cmdp->cmdtype = entry->cmdtype;
6784 cmdp->param = entry->u;
6785 cmdp->rehash = 0;
6786}
6787
6788static int
6789hashcmd(int argc, char **argv)
6790{
6791 struct tblentry **pp;
6792 struct tblentry *cmdp;
6793 int c;
6794 struct cmdentry entry;
6795 char *name;
6796
6797 while ((c = nextopt("r")) != '\0') {
6798 clearcmdentry(0);
6799 return 0;
6800 }
6801 if (*argptr == NULL) {
6802 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6803 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6804 if (cmdp->cmdtype == CMDNORMAL)
6805 printentry(cmdp);
6806 }
6807 }
6808 return 0;
6809 }
6810 c = 0;
6811 while ((name = *argptr) != NULL) {
6812 cmdp = cmdlookup(name, 0);
6813 if (cmdp != NULL
6814 && (cmdp->cmdtype == CMDNORMAL
6815 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6816 delete_cmd_entry();
6817 find_command(name, &entry, DO_ERR, pathval());
6818 if (entry.cmdtype == CMDUNKNOWN)
6819 c = 1;
6820 argptr++;
6821 }
6822 return c;
6823}
6824
6825/*
6826 * Called when a cd is done. Marks all commands so the next time they
6827 * are executed they will be rehashed.
6828 */
6829static void
6830hashcd(void)
6831{
6832 struct tblentry **pp;
6833 struct tblentry *cmdp;
6834
6835 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6836 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6837 if (cmdp->cmdtype == CMDNORMAL || (
6838 cmdp->cmdtype == CMDBUILTIN &&
6839 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6840 builtinloc > 0
6841 ))
6842 cmdp->rehash = 1;
6843 }
6844 }
6845}
6846
6847/*
6848 * Fix command hash table when PATH changed.
6849 * Called before PATH is changed. The argument is the new value of PATH;
6850 * pathval() still returns the old value at this point.
6851 * Called with interrupts off.
6852 */
6853static void
6854changepath(const char *newval)
6855{
6856 const char *old, *new;
6857 int idx;
6858 int firstchange;
6859 int idx_bltin;
6860
6861 old = pathval();
6862 new = newval;
6863 firstchange = 9999; /* assume no change */
6864 idx = 0;
6865 idx_bltin = -1;
6866 for (;;) {
6867 if (*old != *new) {
6868 firstchange = idx;
6869 if ((*old == '\0' && *new == ':')
6870 || (*old == ':' && *new == '\0'))
6871 firstchange++;
6872 old = new; /* ignore subsequent differences */
6873 }
6874 if (*new == '\0')
6875 break;
6876 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6877 idx_bltin = idx;
6878 if (*new == ':') {
6879 idx++;
6880 }
6881 new++, old++;
6882 }
6883 if (builtinloc < 0 && idx_bltin >= 0)
6884 builtinloc = idx_bltin; /* zap builtins */
6885 if (builtinloc >= 0 && idx_bltin < 0)
6886 firstchange = 0;
6887 clearcmdentry(firstchange);
6888 builtinloc = idx_bltin;
6889}
6890
6891#define TEOF 0
6892#define TNL 1
6893#define TREDIR 2
6894#define TWORD 3
6895#define TSEMI 4
6896#define TBACKGND 5
6897#define TAND 6
6898#define TOR 7
6899#define TPIPE 8
6900#define TLP 9
6901#define TRP 10
6902#define TENDCASE 11
6903#define TENDBQUOTE 12
6904#define TNOT 13
6905#define TCASE 14
6906#define TDO 15
6907#define TDONE 16
6908#define TELIF 17
6909#define TELSE 18
6910#define TESAC 19
6911#define TFI 20
6912#define TFOR 21
6913#define TIF 22
6914#define TIN 23
6915#define TTHEN 24
6916#define TUNTIL 25
6917#define TWHILE 26
6918#define TBEGIN 27
6919#define TEND 28
6920
6921/* first char is indicating which tokens mark the end of a list */
6922static const char *const tokname_array[] = {
6923 "\1end of file",
6924 "\0newline",
6925 "\0redirection",
6926 "\0word",
6927 "\0;",
6928 "\0&",
6929 "\0&&",
6930 "\0||",
6931 "\0|",
6932 "\0(",
6933 "\1)",
6934 "\1;;",
6935 "\1`",
6936#define KWDOFFSET 13
6937 /* the following are keywords */
6938 "\0!",
6939 "\0case",
6940 "\1do",
6941 "\1done",
6942 "\1elif",
6943 "\1else",
6944 "\1esac",
6945 "\1fi",
6946 "\0for",
6947 "\0if",
6948 "\0in",
6949 "\1then",
6950 "\0until",
6951 "\0while",
6952 "\0{",
6953 "\1}",
6954};
6955
6956static const char *
6957tokname(int tok)
6958{
6959 static char buf[16];
6960
6961 if (tok >= TSEMI)
6962 buf[0] = '"';
6963 sprintf(buf + (tok >= TSEMI), "%s%c",
6964 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6965 return buf;
6966}
6967
6968/* Wrapper around strcmp for qsort/bsearch/... */
6969static int
6970pstrcmp(const void *a, const void *b)
6971{
6972 return strcmp((const char *) a, (*(const char *const *) b) + 1);
6973}
6974
6975static const char *const *
6976findkwd(const char *s)
6977{
6978 return bsearch(s, tokname_array + KWDOFFSET,
6979 (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
6980 sizeof(const char *), pstrcmp);
6981}
6982
6983/*
6984 * Locate and print what a word is...
6985 */
6986#if ENABLE_ASH_CMDCMD
6987static int
6988describe_command(char *command, int describe_command_verbose)
6989#else
6990#define describe_command_verbose 1
6991static int
6992describe_command(char *command)
6993#endif
6994{
6995 struct cmdentry entry;
6996 struct tblentry *cmdp;
6997#if ENABLE_ASH_ALIAS
6998 const struct alias *ap;
6999#endif
7000 const char *path = pathval();
7001
7002 if (describe_command_verbose) {
7003 out1str(command);
7004 }
7005
7006 /* First look at the keywords */
7007 if (findkwd(command)) {
7008 out1str(describe_command_verbose ? " is a shell keyword" : command);
7009 goto out;
7010 }
7011
7012#if ENABLE_ASH_ALIAS
7013 /* Then look at the aliases */
7014 ap = lookupalias(command, 0);
7015 if (ap != NULL) {
7016 if (describe_command_verbose) {
7017 out1fmt(" is an alias for %s", ap->val);
7018 } else {
7019 out1str("alias ");
7020 printalias(ap);
7021 return 0;
7022 }
7023 goto out;
7024 }
7025#endif
7026 /* Then check if it is a tracked alias */
7027 cmdp = cmdlookup(command, 0);
7028 if (cmdp != NULL) {
7029 entry.cmdtype = cmdp->cmdtype;
7030 entry.u = cmdp->param;
7031 } else {
7032 /* Finally use brute force */
7033 find_command(command, &entry, DO_ABS, path);
7034 }
7035
7036 switch (entry.cmdtype) {
7037 case CMDNORMAL: {
7038 int j = entry.u.index;
7039 char *p;
7040 if (j == -1) {
7041 p = command;
7042 } else {
7043 do {
7044 p = padvance(&path, command);
7045 stunalloc(p);
7046 } while (--j >= 0);
7047 }
7048 if (describe_command_verbose) {
7049 out1fmt(" is%s %s",
7050 (cmdp ? " a tracked alias for" : nullstr), p
7051 );
7052 } else {
7053 out1str(p);
7054 }
7055 break;
7056 }
7057
7058 case CMDFUNCTION:
7059 if (describe_command_verbose) {
7060 out1str(" is a shell function");
7061 } else {
7062 out1str(command);
7063 }
7064 break;
7065
7066 case CMDBUILTIN:
7067 if (describe_command_verbose) {
7068 out1fmt(" is a %sshell builtin",
7069 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7070 "special " : nullstr
7071 );
7072 } else {
7073 out1str(command);
7074 }
7075 break;
7076
7077 default:
7078 if (describe_command_verbose) {
7079 out1str(": not found\n");
7080 }
7081 return 127;
7082 }
7083 out:
7084 outstr("\n", stdout);
7085 return 0;
7086}
7087
7088static int
7089typecmd(int argc, char **argv)
7090{
7091 int i;
7092 int err = 0;
7093
7094 for (i = 1; i < argc; i++) {
7095#if ENABLE_ASH_CMDCMD
7096 err |= describe_command(argv[i], 1);
7097#else
7098 err |= describe_command(argv[i]);
7099#endif
7100 }
7101 return err;
7102}
7103
7104#if ENABLE_ASH_CMDCMD
7105static int
7106commandcmd(int argc, char **argv)
7107{
7108 int c;
7109 enum {
7110 VERIFY_BRIEF = 1,
7111 VERIFY_VERBOSE = 2,
7112 } verify = 0;
7113
7114 while ((c = nextopt("pvV")) != '\0')
7115 if (c == 'V')
7116 verify |= VERIFY_VERBOSE;
7117 else if (c == 'v')
7118 verify |= VERIFY_BRIEF;
7119#if DEBUG
7120 else if (c != 'p')
7121 abort();
7122#endif
7123 if (verify)
7124 return describe_command(*argptr, verify - VERIFY_BRIEF);
7125
7126 return 0;
7127}
7128#endif
7129
7130
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007131/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007132
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007133static int funcblocksize; /* size of structures in function */
7134static int funcstringsize; /* size of strings in node */
7135static void *funcblock; /* block to allocate function from */
7136static char *funcstring; /* block to allocate strings from */
7137
Eric Andersencb57d552001-06-28 07:25:16 +00007138/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007139#define EV_EXIT 01 /* exit after evaluating tree */
7140#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7141#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007142
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007143static const short nodesize[26] = {
7144 SHELL_ALIGN(sizeof(struct ncmd)),
7145 SHELL_ALIGN(sizeof(struct npipe)),
7146 SHELL_ALIGN(sizeof(struct nredir)),
7147 SHELL_ALIGN(sizeof(struct nredir)),
7148 SHELL_ALIGN(sizeof(struct nredir)),
7149 SHELL_ALIGN(sizeof(struct nbinary)),
7150 SHELL_ALIGN(sizeof(struct nbinary)),
7151 SHELL_ALIGN(sizeof(struct nbinary)),
7152 SHELL_ALIGN(sizeof(struct nif)),
7153 SHELL_ALIGN(sizeof(struct nbinary)),
7154 SHELL_ALIGN(sizeof(struct nbinary)),
7155 SHELL_ALIGN(sizeof(struct nfor)),
7156 SHELL_ALIGN(sizeof(struct ncase)),
7157 SHELL_ALIGN(sizeof(struct nclist)),
7158 SHELL_ALIGN(sizeof(struct narg)),
7159 SHELL_ALIGN(sizeof(struct narg)),
7160 SHELL_ALIGN(sizeof(struct nfile)),
7161 SHELL_ALIGN(sizeof(struct nfile)),
7162 SHELL_ALIGN(sizeof(struct nfile)),
7163 SHELL_ALIGN(sizeof(struct nfile)),
7164 SHELL_ALIGN(sizeof(struct nfile)),
7165 SHELL_ALIGN(sizeof(struct ndup)),
7166 SHELL_ALIGN(sizeof(struct ndup)),
7167 SHELL_ALIGN(sizeof(struct nhere)),
7168 SHELL_ALIGN(sizeof(struct nhere)),
7169 SHELL_ALIGN(sizeof(struct nnot)),
7170};
7171
7172static void calcsize(union node *n);
7173
7174static void
7175sizenodelist(struct nodelist *lp)
7176{
7177 while (lp) {
7178 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7179 calcsize(lp->n);
7180 lp = lp->next;
7181 }
7182}
7183
7184static void
7185calcsize(union node *n)
7186{
7187 if (n == NULL)
7188 return;
7189 funcblocksize += nodesize[n->type];
7190 switch (n->type) {
7191 case NCMD:
7192 calcsize(n->ncmd.redirect);
7193 calcsize(n->ncmd.args);
7194 calcsize(n->ncmd.assign);
7195 break;
7196 case NPIPE:
7197 sizenodelist(n->npipe.cmdlist);
7198 break;
7199 case NREDIR:
7200 case NBACKGND:
7201 case NSUBSHELL:
7202 calcsize(n->nredir.redirect);
7203 calcsize(n->nredir.n);
7204 break;
7205 case NAND:
7206 case NOR:
7207 case NSEMI:
7208 case NWHILE:
7209 case NUNTIL:
7210 calcsize(n->nbinary.ch2);
7211 calcsize(n->nbinary.ch1);
7212 break;
7213 case NIF:
7214 calcsize(n->nif.elsepart);
7215 calcsize(n->nif.ifpart);
7216 calcsize(n->nif.test);
7217 break;
7218 case NFOR:
7219 funcstringsize += strlen(n->nfor.var) + 1;
7220 calcsize(n->nfor.body);
7221 calcsize(n->nfor.args);
7222 break;
7223 case NCASE:
7224 calcsize(n->ncase.cases);
7225 calcsize(n->ncase.expr);
7226 break;
7227 case NCLIST:
7228 calcsize(n->nclist.body);
7229 calcsize(n->nclist.pattern);
7230 calcsize(n->nclist.next);
7231 break;
7232 case NDEFUN:
7233 case NARG:
7234 sizenodelist(n->narg.backquote);
7235 funcstringsize += strlen(n->narg.text) + 1;
7236 calcsize(n->narg.next);
7237 break;
7238 case NTO:
7239 case NCLOBBER:
7240 case NFROM:
7241 case NFROMTO:
7242 case NAPPEND:
7243 calcsize(n->nfile.fname);
7244 calcsize(n->nfile.next);
7245 break;
7246 case NTOFD:
7247 case NFROMFD:
7248 calcsize(n->ndup.vname);
7249 calcsize(n->ndup.next);
7250 break;
7251 case NHERE:
7252 case NXHERE:
7253 calcsize(n->nhere.doc);
7254 calcsize(n->nhere.next);
7255 break;
7256 case NNOT:
7257 calcsize(n->nnot.com);
7258 break;
7259 };
7260}
7261
7262static char *
7263nodeckstrdup(char *s)
7264{
7265 char *rtn = funcstring;
7266
7267 strcpy(funcstring, s);
7268 funcstring += strlen(s) + 1;
7269 return rtn;
7270}
7271
7272static union node *copynode(union node *);
7273
7274static struct nodelist *
7275copynodelist(struct nodelist *lp)
7276{
7277 struct nodelist *start;
7278 struct nodelist **lpp;
7279
7280 lpp = &start;
7281 while (lp) {
7282 *lpp = funcblock;
7283 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7284 (*lpp)->n = copynode(lp->n);
7285 lp = lp->next;
7286 lpp = &(*lpp)->next;
7287 }
7288 *lpp = NULL;
7289 return start;
7290}
7291
7292static union node *
7293copynode(union node *n)
7294{
7295 union node *new;
7296
7297 if (n == NULL)
7298 return NULL;
7299 new = funcblock;
7300 funcblock = (char *) funcblock + nodesize[n->type];
7301
7302 switch (n->type) {
7303 case NCMD:
7304 new->ncmd.redirect = copynode(n->ncmd.redirect);
7305 new->ncmd.args = copynode(n->ncmd.args);
7306 new->ncmd.assign = copynode(n->ncmd.assign);
7307 break;
7308 case NPIPE:
7309 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7310 new->npipe.backgnd = n->npipe.backgnd;
7311 break;
7312 case NREDIR:
7313 case NBACKGND:
7314 case NSUBSHELL:
7315 new->nredir.redirect = copynode(n->nredir.redirect);
7316 new->nredir.n = copynode(n->nredir.n);
7317 break;
7318 case NAND:
7319 case NOR:
7320 case NSEMI:
7321 case NWHILE:
7322 case NUNTIL:
7323 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7324 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7325 break;
7326 case NIF:
7327 new->nif.elsepart = copynode(n->nif.elsepart);
7328 new->nif.ifpart = copynode(n->nif.ifpart);
7329 new->nif.test = copynode(n->nif.test);
7330 break;
7331 case NFOR:
7332 new->nfor.var = nodeckstrdup(n->nfor.var);
7333 new->nfor.body = copynode(n->nfor.body);
7334 new->nfor.args = copynode(n->nfor.args);
7335 break;
7336 case NCASE:
7337 new->ncase.cases = copynode(n->ncase.cases);
7338 new->ncase.expr = copynode(n->ncase.expr);
7339 break;
7340 case NCLIST:
7341 new->nclist.body = copynode(n->nclist.body);
7342 new->nclist.pattern = copynode(n->nclist.pattern);
7343 new->nclist.next = copynode(n->nclist.next);
7344 break;
7345 case NDEFUN:
7346 case NARG:
7347 new->narg.backquote = copynodelist(n->narg.backquote);
7348 new->narg.text = nodeckstrdup(n->narg.text);
7349 new->narg.next = copynode(n->narg.next);
7350 break;
7351 case NTO:
7352 case NCLOBBER:
7353 case NFROM:
7354 case NFROMTO:
7355 case NAPPEND:
7356 new->nfile.fname = copynode(n->nfile.fname);
7357 new->nfile.fd = n->nfile.fd;
7358 new->nfile.next = copynode(n->nfile.next);
7359 break;
7360 case NTOFD:
7361 case NFROMFD:
7362 new->ndup.vname = copynode(n->ndup.vname);
7363 new->ndup.dupfd = n->ndup.dupfd;
7364 new->ndup.fd = n->ndup.fd;
7365 new->ndup.next = copynode(n->ndup.next);
7366 break;
7367 case NHERE:
7368 case NXHERE:
7369 new->nhere.doc = copynode(n->nhere.doc);
7370 new->nhere.fd = n->nhere.fd;
7371 new->nhere.next = copynode(n->nhere.next);
7372 break;
7373 case NNOT:
7374 new->nnot.com = copynode(n->nnot.com);
7375 break;
7376 };
7377 new->type = n->type;
7378 return new;
7379}
7380
7381/*
7382 * Make a copy of a parse tree.
7383 */
7384static struct funcnode *
7385copyfunc(union node *n)
7386{
7387 struct funcnode *f;
7388 size_t blocksize;
7389
7390 funcblocksize = offsetof(struct funcnode, n);
7391 funcstringsize = 0;
7392 calcsize(n);
7393 blocksize = funcblocksize;
7394 f = ckmalloc(blocksize + funcstringsize);
7395 funcblock = (char *) f + offsetof(struct funcnode, n);
7396 funcstring = (char *) f + blocksize;
7397 copynode(n);
7398 f->count = 0;
7399 return f;
7400}
7401
7402/*
7403 * Define a shell function.
7404 */
7405static void
7406defun(char *name, union node *func)
7407{
7408 struct cmdentry entry;
7409
7410 INT_OFF;
7411 entry.cmdtype = CMDFUNCTION;
7412 entry.u.func = copyfunc(func);
7413 addcmdentry(name, &entry);
7414 INT_ON;
7415}
7416
7417static int evalskip; /* set if we are skipping commands */
7418/* reasons for skipping commands (see comment on breakcmd routine) */
7419#define SKIPBREAK (1 << 0)
7420#define SKIPCONT (1 << 1)
7421#define SKIPFUNC (1 << 2)
7422#define SKIPFILE (1 << 3)
7423#define SKIPEVAL (1 << 4)
7424static int skipcount; /* number of levels to skip */
7425static int funcnest; /* depth of function calls */
7426
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007427/* forward decl way out to parsing code - dotrap needs it */
7428static int evalstring(char *s, int mask);
7429
7430/*
7431 * Called to execute a trap. Perhaps we should avoid entering new trap
7432 * handlers while we are executing a trap handler.
7433 */
7434static int
7435dotrap(void)
7436{
7437 char *p;
7438 char *q;
7439 int i;
7440 int savestatus;
7441 int skip = 0;
7442
7443 savestatus = exitstatus;
7444 pendingsigs = 0;
7445 xbarrier();
7446
7447 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7448 if (!*q)
7449 continue;
7450 *q = '\0';
7451
7452 p = trap[i + 1];
7453 if (!p)
7454 continue;
7455 skip = evalstring(p, SKIPEVAL);
7456 exitstatus = savestatus;
7457 if (skip)
7458 break;
7459 }
7460
7461 return skip;
7462}
7463
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007464/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007465static void evalloop(union node *, int);
7466static void evalfor(union node *, int);
7467static void evalcase(union node *, int);
7468static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007469static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007470static void evalpipe(union node *, int);
7471static void evalcommand(union node *, int);
7472static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007473static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007474
Eric Andersen62483552001-07-10 06:09:16 +00007475/*
Eric Andersenc470f442003-07-28 09:56:35 +00007476 * Evaluate a parse tree. The value is left in the global variable
7477 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007478 */
Eric Andersenc470f442003-07-28 09:56:35 +00007479static void
7480evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007481{
Eric Andersenc470f442003-07-28 09:56:35 +00007482 int checkexit = 0;
7483 void (*evalfn)(union node *, int);
7484 unsigned isor;
7485 int status;
7486 if (n == NULL) {
7487 TRACE(("evaltree(NULL) called\n"));
7488 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007489 }
Eric Andersenc470f442003-07-28 09:56:35 +00007490 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007491 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007492 switch (n->type) {
7493 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007494#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007495 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007496 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007497 break;
7498#endif
7499 case NNOT:
7500 evaltree(n->nnot.com, EV_TESTED);
7501 status = !exitstatus;
7502 goto setstatus;
7503 case NREDIR:
7504 expredir(n->nredir.redirect);
7505 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7506 if (!status) {
7507 evaltree(n->nredir.n, flags & EV_TESTED);
7508 status = exitstatus;
7509 }
7510 popredir(0);
7511 goto setstatus;
7512 case NCMD:
7513 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007514 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007515 if (eflag && !(flags & EV_TESTED))
7516 checkexit = ~0;
7517 goto calleval;
7518 case NFOR:
7519 evalfn = evalfor;
7520 goto calleval;
7521 case NWHILE:
7522 case NUNTIL:
7523 evalfn = evalloop;
7524 goto calleval;
7525 case NSUBSHELL:
7526 case NBACKGND:
7527 evalfn = evalsubshell;
7528 goto calleval;
7529 case NPIPE:
7530 evalfn = evalpipe;
7531 goto checkexit;
7532 case NCASE:
7533 evalfn = evalcase;
7534 goto calleval;
7535 case NAND:
7536 case NOR:
7537 case NSEMI:
7538#if NAND + 1 != NOR
7539#error NAND + 1 != NOR
7540#endif
7541#if NOR + 1 != NSEMI
7542#error NOR + 1 != NSEMI
7543#endif
7544 isor = n->type - NAND;
7545 evaltree(
7546 n->nbinary.ch1,
7547 (flags | ((isor >> 1) - 1)) & EV_TESTED
7548 );
7549 if (!exitstatus == isor)
7550 break;
7551 if (!evalskip) {
7552 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007553 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007554 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007555 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007556 evalfn(n, flags);
7557 break;
7558 }
7559 break;
7560 case NIF:
7561 evaltree(n->nif.test, EV_TESTED);
7562 if (evalskip)
7563 break;
7564 if (exitstatus == 0) {
7565 n = n->nif.ifpart;
7566 goto evaln;
7567 } else if (n->nif.elsepart) {
7568 n = n->nif.elsepart;
7569 goto evaln;
7570 }
7571 goto success;
7572 case NDEFUN:
7573 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007574 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007575 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007576 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007577 exitstatus = status;
7578 break;
7579 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007580 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007581 if ((checkexit & exitstatus))
7582 evalskip |= SKIPEVAL;
7583 else if (pendingsigs && dotrap())
7584 goto exexit;
7585
7586 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007587 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007588 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007589 }
Eric Andersen62483552001-07-10 06:09:16 +00007590}
7591
Eric Andersenc470f442003-07-28 09:56:35 +00007592#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7593static
7594#endif
7595void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7596
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007597static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007598
7599static void
7600evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007601{
7602 int status;
7603
7604 loopnest++;
7605 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007606 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007607 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007608 int i;
7609
Eric Andersencb57d552001-06-28 07:25:16 +00007610 evaltree(n->nbinary.ch1, EV_TESTED);
7611 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007612 skipping:
7613 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007614 evalskip = 0;
7615 continue;
7616 }
7617 if (evalskip == SKIPBREAK && --skipcount <= 0)
7618 evalskip = 0;
7619 break;
7620 }
Eric Andersenc470f442003-07-28 09:56:35 +00007621 i = exitstatus;
7622 if (n->type != NWHILE)
7623 i = !i;
7624 if (i != 0)
7625 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007626 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007627 status = exitstatus;
7628 if (evalskip)
7629 goto skipping;
7630 }
7631 loopnest--;
7632 exitstatus = status;
7633}
7634
Eric Andersenc470f442003-07-28 09:56:35 +00007635static void
7636evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007637{
7638 struct arglist arglist;
7639 union node *argp;
7640 struct strlist *sp;
7641 struct stackmark smark;
7642
7643 setstackmark(&smark);
7644 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007645 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007646 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007647 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007648 if (evalskip)
7649 goto out;
7650 }
7651 *arglist.lastp = NULL;
7652
7653 exitstatus = 0;
7654 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007655 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007656 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007657 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007658 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007659 if (evalskip) {
7660 if (evalskip == SKIPCONT && --skipcount <= 0) {
7661 evalskip = 0;
7662 continue;
7663 }
7664 if (evalskip == SKIPBREAK && --skipcount <= 0)
7665 evalskip = 0;
7666 break;
7667 }
7668 }
7669 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007670 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007671 popstackmark(&smark);
7672}
7673
Eric Andersenc470f442003-07-28 09:56:35 +00007674static void
7675evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007676{
7677 union node *cp;
7678 union node *patp;
7679 struct arglist arglist;
7680 struct stackmark smark;
7681
7682 setstackmark(&smark);
7683 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007684 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007685 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007686 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7687 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007688 if (casematch(patp, arglist.list->text)) {
7689 if (evalskip == 0) {
7690 evaltree(cp->nclist.body, flags);
7691 }
7692 goto out;
7693 }
7694 }
7695 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007696 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007697 popstackmark(&smark);
7698}
7699
Eric Andersenc470f442003-07-28 09:56:35 +00007700/*
7701 * Kick off a subshell to evaluate a tree.
7702 */
Eric Andersenc470f442003-07-28 09:56:35 +00007703static void
7704evalsubshell(union node *n, int flags)
7705{
7706 struct job *jp;
7707 int backgnd = (n->type == NBACKGND);
7708 int status;
7709
7710 expredir(n->nredir.redirect);
7711 if (!backgnd && flags & EV_EXIT && !trap[0])
7712 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007713 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007714 jp = makejob(n, 1);
7715 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007716 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007717 flags |= EV_EXIT;
7718 if (backgnd)
7719 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007720 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007721 redirect(n->nredir.redirect, 0);
7722 evaltreenr(n->nredir.n, flags);
7723 /* never returns */
7724 }
7725 status = 0;
7726 if (! backgnd)
7727 status = waitforjob(jp);
7728 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007729 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007730}
7731
Eric Andersenc470f442003-07-28 09:56:35 +00007732/*
7733 * Compute the names of the files in a redirection list.
7734 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007735static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007736static void
7737expredir(union node *n)
7738{
7739 union node *redir;
7740
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007741 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007742 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007743
7744 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007745 fn.lastp = &fn.list;
7746 switch (redir->type) {
7747 case NFROMTO:
7748 case NFROM:
7749 case NTO:
7750 case NCLOBBER:
7751 case NAPPEND:
7752 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7753 redir->nfile.expfname = fn.list->text;
7754 break;
7755 case NFROMFD:
7756 case NTOFD:
7757 if (redir->ndup.vname) {
7758 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007759 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007760 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007761 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007762 }
7763 break;
7764 }
7765 }
7766}
7767
Eric Andersencb57d552001-06-28 07:25:16 +00007768/*
Eric Andersencb57d552001-06-28 07:25:16 +00007769 * Evaluate a pipeline. All the processes in the pipeline are children
7770 * of the process creating the pipeline. (This differs from some versions
7771 * of the shell, which make the last process in a pipeline the parent
7772 * of all the rest.)
7773 */
Eric Andersenc470f442003-07-28 09:56:35 +00007774static void
7775evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007776{
7777 struct job *jp;
7778 struct nodelist *lp;
7779 int pipelen;
7780 int prevfd;
7781 int pip[2];
7782
Eric Andersenc470f442003-07-28 09:56:35 +00007783 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007784 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007785 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007786 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007787 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007788 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007789 jp = makejob(n, pipelen);
7790 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007791 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007792 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007793 pip[1] = -1;
7794 if (lp->next) {
7795 if (pipe(pip) < 0) {
7796 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007797 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007798 }
7799 }
7800 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007801 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007802 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007803 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007804 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007805 if (prevfd > 0) {
7806 dup2(prevfd, 0);
7807 close(prevfd);
7808 }
7809 if (pip[1] > 1) {
7810 dup2(pip[1], 1);
7811 close(pip[1]);
7812 }
Eric Andersenc470f442003-07-28 09:56:35 +00007813 evaltreenr(lp->n, flags);
7814 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007815 }
7816 if (prevfd >= 0)
7817 close(prevfd);
7818 prevfd = pip[0];
7819 close(pip[1]);
7820 }
Eric Andersencb57d552001-06-28 07:25:16 +00007821 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007822 exitstatus = waitforjob(jp);
7823 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007824 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007825 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007826}
7827
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007828/*
7829 * Controls whether the shell is interactive or not.
7830 */
7831static void
7832setinteractive(int on)
7833{
7834 static int is_interactive;
7835
7836 if (++on == is_interactive)
7837 return;
7838 is_interactive = on;
7839 setsignal(SIGINT);
7840 setsignal(SIGQUIT);
7841 setsignal(SIGTERM);
7842#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7843 if (is_interactive > 1) {
7844 /* Looks like they want an interactive shell */
7845 static smallint do_banner;
7846
7847 if (!do_banner) {
7848 out1fmt(
7849 "\n\n"
7850 "%s Built-in shell (ash)\n"
7851 "Enter 'help' for a list of built-in commands."
7852 "\n\n",
7853 BB_BANNER);
7854 do_banner = 1;
7855 }
7856 }
7857#endif
7858}
7859
7860#if ENABLE_FEATURE_EDITING_VI
7861#define setvimode(on) do { \
7862 if (on) line_input_state->flags |= VI_MODE; \
7863 else line_input_state->flags &= ~VI_MODE; \
7864} while (0)
7865#else
7866#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7867#endif
7868
7869static void
7870optschanged(void)
7871{
7872#if DEBUG
7873 opentrace();
7874#endif
7875 setinteractive(iflag);
7876 setjobctl(mflag);
7877 setvimode(viflag);
7878}
7879
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007880static struct localvar *localvars;
7881
7882/*
7883 * Called after a function returns.
7884 * Interrupts must be off.
7885 */
7886static void
7887poplocalvars(void)
7888{
7889 struct localvar *lvp;
7890 struct var *vp;
7891
7892 while ((lvp = localvars) != NULL) {
7893 localvars = lvp->next;
7894 vp = lvp->vp;
7895 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7896 if (vp == NULL) { /* $- saved */
7897 memcpy(optlist, lvp->text, sizeof(optlist));
7898 free((char*)lvp->text);
7899 optschanged();
7900 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7901 unsetvar(vp->text);
7902 } else {
7903 if (vp->func)
7904 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7905 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7906 free((char*)vp->text);
7907 vp->flags = lvp->flags;
7908 vp->text = lvp->text;
7909 }
7910 free(lvp);
7911 }
7912}
7913
7914static int
7915evalfun(struct funcnode *func, int argc, char **argv, int flags)
7916{
7917 volatile struct shparam saveparam;
7918 struct localvar *volatile savelocalvars;
7919 struct jmploc *volatile savehandler;
7920 struct jmploc jmploc;
7921 int e;
7922
7923 saveparam = shellparam;
7924 savelocalvars = localvars;
7925 e = setjmp(jmploc.loc);
7926 if (e) {
7927 goto funcdone;
7928 }
7929 INT_OFF;
7930 savehandler = exception_handler;
7931 exception_handler = &jmploc;
7932 localvars = NULL;
7933 shellparam.malloc = 0;
7934 func->count++;
7935 funcnest++;
7936 INT_ON;
7937 shellparam.nparam = argc - 1;
7938 shellparam.p = argv + 1;
7939#if ENABLE_ASH_GETOPTS
7940 shellparam.optind = 1;
7941 shellparam.optoff = -1;
7942#endif
7943 evaltree(&func->n, flags & EV_TESTED);
7944funcdone:
7945 INT_OFF;
7946 funcnest--;
7947 freefunc(func);
7948 poplocalvars();
7949 localvars = savelocalvars;
7950 freeparam(&shellparam);
7951 shellparam = saveparam;
7952 exception_handler = savehandler;
7953 INT_ON;
7954 evalskip &= ~SKIPFUNC;
7955 return e;
7956}
7957
Denis Vlasenko131ae172007-02-18 13:00:19 +00007958#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007959static char **
7960parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007961{
7962 char *cp, c;
7963
7964 for (;;) {
7965 cp = *++argv;
7966 if (!cp)
7967 return 0;
7968 if (*cp++ != '-')
7969 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007970 c = *cp++;
7971 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007972 break;
7973 if (c == '-' && !*cp) {
7974 argv++;
7975 break;
7976 }
7977 do {
7978 switch (c) {
7979 case 'p':
7980 *path = defpath;
7981 break;
7982 default:
7983 /* run 'typecmd' for other options */
7984 return 0;
7985 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007986 c = *cp++;
7987 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007988 }
7989 return argv;
7990}
7991#endif
7992
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007993/*
7994 * Make a variable a local variable. When a variable is made local, it's
7995 * value and flags are saved in a localvar structure. The saved values
7996 * will be restored when the shell function returns. We handle the name
7997 * "-" as a special case.
7998 */
7999static void
8000mklocal(char *name)
8001{
8002 struct localvar *lvp;
8003 struct var **vpp;
8004 struct var *vp;
8005
8006 INT_OFF;
8007 lvp = ckmalloc(sizeof(struct localvar));
8008 if (LONE_DASH(name)) {
8009 char *p;
8010 p = ckmalloc(sizeof(optlist));
8011 lvp->text = memcpy(p, optlist, sizeof(optlist));
8012 vp = NULL;
8013 } else {
8014 char *eq;
8015
8016 vpp = hashvar(name);
8017 vp = *findvar(vpp, name);
8018 eq = strchr(name, '=');
8019 if (vp == NULL) {
8020 if (eq)
8021 setvareq(name, VSTRFIXED);
8022 else
8023 setvar(name, NULL, VSTRFIXED);
8024 vp = *vpp; /* the new variable */
8025 lvp->flags = VUNSET;
8026 } else {
8027 lvp->text = vp->text;
8028 lvp->flags = vp->flags;
8029 vp->flags |= VSTRFIXED|VTEXTFIXED;
8030 if (eq)
8031 setvareq(name, 0);
8032 }
8033 }
8034 lvp->vp = vp;
8035 lvp->next = localvars;
8036 localvars = lvp;
8037 INT_ON;
8038}
8039
8040/*
8041 * The "local" command.
8042 */
8043static int
8044localcmd(int argc, char **argv)
8045{
8046 char *name;
8047
8048 argv = argptr;
8049 while ((name = *argv++) != NULL) {
8050 mklocal(name);
8051 }
8052 return 0;
8053}
8054
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008055static int
8056falsecmd(int argc, char **argv)
8057{
8058 return 1;
8059}
8060
8061static int
8062truecmd(int argc, char **argv)
8063{
8064 return 0;
8065}
8066
8067static int
8068execcmd(int argc, char **argv)
8069{
8070 if (argc > 1) {
8071 iflag = 0; /* exit on error */
8072 mflag = 0;
8073 optschanged();
8074 shellexec(argv + 1, pathval(), 0);
8075 }
8076 return 0;
8077}
8078
8079/*
8080 * The return command.
8081 */
8082static int
8083returncmd(int argc, char **argv)
8084{
8085 /*
8086 * If called outside a function, do what ksh does;
8087 * skip the rest of the file.
8088 */
8089 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8090 return argv[1] ? number(argv[1]) : exitstatus;
8091}
8092
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008093/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008094static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008095static int dotcmd(int, char **);
8096static int evalcmd(int, char **);
8097#if ENABLE_ASH_BUILTIN_ECHO
8098static int echocmd(int, char **);
8099#endif
8100#if ENABLE_ASH_BUILTIN_TEST
8101static int testcmd(int, char **);
8102#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008103static int exitcmd(int, char **);
8104static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008105#if ENABLE_ASH_GETOPTS
8106static int getoptscmd(int, char **);
8107#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008108#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8109static int helpcmd(int argc, char **argv);
8110#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008111#if ENABLE_ASH_MATH_SUPPORT
8112static int letcmd(int, char **);
8113#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008114static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008115static int setcmd(int, char **);
8116static int shiftcmd(int, char **);
8117static int timescmd(int, char **);
8118static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008119static int umaskcmd(int, char **);
8120static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008121static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008122
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008123#define BUILTIN_NOSPEC "0"
8124#define BUILTIN_SPECIAL "1"
8125#define BUILTIN_REGULAR "2"
8126#define BUILTIN_SPEC_REG "3"
8127#define BUILTIN_ASSIGN "4"
8128#define BUILTIN_SPEC_ASSG "5"
8129#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008130#define BUILTIN_SPEC_REG_ASSG "7"
8131
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008132/* make sure to keep these in proper order since it is searched via bsearch() */
8133static const struct builtincmd builtintab[] = {
8134 { BUILTIN_SPEC_REG ".", dotcmd },
8135 { BUILTIN_SPEC_REG ":", truecmd },
8136#if ENABLE_ASH_BUILTIN_TEST
8137 { BUILTIN_REGULAR "[", testcmd },
8138 { BUILTIN_REGULAR "[[", testcmd },
8139#endif
8140#if ENABLE_ASH_ALIAS
8141 { BUILTIN_REG_ASSG "alias", aliascmd },
8142#endif
8143#if JOBS
8144 { BUILTIN_REGULAR "bg", fg_bgcmd },
8145#endif
8146 { BUILTIN_SPEC_REG "break", breakcmd },
8147 { BUILTIN_REGULAR "cd", cdcmd },
8148 { BUILTIN_NOSPEC "chdir", cdcmd },
8149#if ENABLE_ASH_CMDCMD
8150 { BUILTIN_REGULAR "command", commandcmd },
8151#endif
8152 { BUILTIN_SPEC_REG "continue", breakcmd },
8153#if ENABLE_ASH_BUILTIN_ECHO
8154 { BUILTIN_REGULAR "echo", echocmd },
8155#endif
8156 { BUILTIN_SPEC_REG "eval", evalcmd },
8157 { BUILTIN_SPEC_REG "exec", execcmd },
8158 { BUILTIN_SPEC_REG "exit", exitcmd },
8159 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8160 { BUILTIN_REGULAR "false", falsecmd },
8161#if JOBS
8162 { BUILTIN_REGULAR "fg", fg_bgcmd },
8163#endif
8164#if ENABLE_ASH_GETOPTS
8165 { BUILTIN_REGULAR "getopts", getoptscmd },
8166#endif
8167 { BUILTIN_NOSPEC "hash", hashcmd },
8168#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8169 { BUILTIN_NOSPEC "help", helpcmd },
8170#endif
8171#if JOBS
8172 { BUILTIN_REGULAR "jobs", jobscmd },
8173 { BUILTIN_REGULAR "kill", killcmd },
8174#endif
8175#if ENABLE_ASH_MATH_SUPPORT
8176 { BUILTIN_NOSPEC "let", letcmd },
8177#endif
8178 { BUILTIN_ASSIGN "local", localcmd },
8179 { BUILTIN_NOSPEC "pwd", pwdcmd },
8180 { BUILTIN_REGULAR "read", readcmd },
8181 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8182 { BUILTIN_SPEC_REG "return", returncmd },
8183 { BUILTIN_SPEC_REG "set", setcmd },
8184 { BUILTIN_SPEC_REG "shift", shiftcmd },
8185 { BUILTIN_SPEC_REG "source", dotcmd },
8186#if ENABLE_ASH_BUILTIN_TEST
8187 { BUILTIN_REGULAR "test", testcmd },
8188#endif
8189 { BUILTIN_SPEC_REG "times", timescmd },
8190 { BUILTIN_SPEC_REG "trap", trapcmd },
8191 { BUILTIN_REGULAR "true", truecmd },
8192 { BUILTIN_NOSPEC "type", typecmd },
8193 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8194 { BUILTIN_REGULAR "umask", umaskcmd },
8195#if ENABLE_ASH_ALIAS
8196 { BUILTIN_REGULAR "unalias", unaliascmd },
8197#endif
8198 { BUILTIN_SPEC_REG "unset", unsetcmd },
8199 { BUILTIN_REGULAR "wait", waitcmd },
8200};
8201
8202#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
8203
8204#define COMMANDCMD (builtintab + 5 + \
8205 2 * ENABLE_ASH_BUILTIN_TEST + \
8206 ENABLE_ASH_ALIAS + \
8207 ENABLE_ASH_JOB_CONTROL)
8208#define EXECCMD (builtintab + 7 + \
8209 2 * ENABLE_ASH_BUILTIN_TEST + \
8210 ENABLE_ASH_ALIAS + \
8211 ENABLE_ASH_JOB_CONTROL + \
8212 ENABLE_ASH_CMDCMD + \
8213 ENABLE_ASH_BUILTIN_ECHO)
8214
8215/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008216 * Search the table of builtin commands.
8217 */
8218static struct builtincmd *
8219find_builtin(const char *name)
8220{
8221 struct builtincmd *bp;
8222
8223 bp = bsearch(
8224 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
8225 pstrcmp
8226 );
8227 return bp;
8228}
8229
8230/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008231 * Execute a simple command.
8232 */
8233static int back_exitstatus; /* exit status of backquoted command */
8234static int
8235isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008236{
8237 const char *q = endofname(p);
8238 if (p == q)
8239 return 0;
8240 return *q == '=';
8241}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008242static int
8243bltincmd(int argc, char **argv)
8244{
8245 /* Preserve exitstatus of a previous possible redirection
8246 * as POSIX mandates */
8247 return back_exitstatus;
8248}
Eric Andersenc470f442003-07-28 09:56:35 +00008249static void
8250evalcommand(union node *cmd, int flags)
8251{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008252 static const struct builtincmd bltin = {
8253 "\0\0", bltincmd
8254 };
Eric Andersenc470f442003-07-28 09:56:35 +00008255 struct stackmark smark;
8256 union node *argp;
8257 struct arglist arglist;
8258 struct arglist varlist;
8259 char **argv;
8260 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008261 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008262 struct cmdentry cmdentry;
8263 struct job *jp;
8264 char *lastarg;
8265 const char *path;
8266 int spclbltin;
8267 int cmd_is_exec;
8268 int status;
8269 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008270 struct builtincmd *bcmd;
8271 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008272
8273 /* First expand the arguments. */
8274 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8275 setstackmark(&smark);
8276 back_exitstatus = 0;
8277
8278 cmdentry.cmdtype = CMDBUILTIN;
8279 cmdentry.u.cmd = &bltin;
8280 varlist.lastp = &varlist.list;
8281 *varlist.lastp = NULL;
8282 arglist.lastp = &arglist.list;
8283 *arglist.lastp = NULL;
8284
8285 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008286 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008287 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8288 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8289 }
8290
Eric Andersenc470f442003-07-28 09:56:35 +00008291 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8292 struct strlist **spp;
8293
8294 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008295 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008296 expandarg(argp, &arglist, EXP_VARTILDE);
8297 else
8298 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8299
Eric Andersenc470f442003-07-28 09:56:35 +00008300 for (sp = *spp; sp; sp = sp->next)
8301 argc++;
8302 }
8303
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008304 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008305 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008306 TRACE(("evalcommand arg: %s\n", sp->text));
8307 *nargv++ = sp->text;
8308 }
8309 *nargv = NULL;
8310
8311 lastarg = NULL;
8312 if (iflag && funcnest == 0 && argc > 0)
8313 lastarg = nargv[-1];
8314
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008315 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008316 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008317 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008318
8319 path = vpath.text;
8320 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8321 struct strlist **spp;
8322 char *p;
8323
8324 spp = varlist.lastp;
8325 expandarg(argp, &varlist, EXP_VARTILDE);
8326
8327 /*
8328 * Modify the command lookup path, if a PATH= assignment
8329 * is present
8330 */
8331 p = (*spp)->text;
8332 if (varequal(p, path))
8333 path = p;
8334 }
8335
8336 /* Print the command if xflag is set. */
8337 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008338 int n;
8339 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008340
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008341 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008342 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008343
8344 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008345 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008346 while (sp) {
8347 dprintf(preverrout_fd, p, sp->text);
8348 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008349 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008350 p--;
8351 }
8352 }
8353 sp = arglist.list;
8354 }
Rob Landley53437472006-07-16 08:14:35 +00008355 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008356 }
8357
8358 cmd_is_exec = 0;
8359 spclbltin = -1;
8360
8361 /* Now locate the command. */
8362 if (argc) {
8363 const char *oldpath;
8364 int cmd_flag = DO_ERR;
8365
8366 path += 5;
8367 oldpath = path;
8368 for (;;) {
8369 find_command(argv[0], &cmdentry, cmd_flag, path);
8370 if (cmdentry.cmdtype == CMDUNKNOWN) {
8371 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008372 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008373 goto bail;
8374 }
8375
8376 /* implement bltin and command here */
8377 if (cmdentry.cmdtype != CMDBUILTIN)
8378 break;
8379 if (spclbltin < 0)
8380 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8381 if (cmdentry.u.cmd == EXECCMD)
8382 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008383#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008384 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008385 path = oldpath;
8386 nargv = parse_command_args(argv, &path);
8387 if (!nargv)
8388 break;
8389 argc -= nargv - argv;
8390 argv = nargv;
8391 cmd_flag |= DO_NOFUNC;
8392 } else
8393#endif
8394 break;
8395 }
8396 }
8397
8398 if (status) {
8399 /* We have a redirection error. */
8400 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008401 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008402 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008403 exitstatus = status;
8404 goto out;
8405 }
8406
8407 /* Execute the command. */
8408 switch (cmdentry.cmdtype) {
8409 default:
8410 /* Fork off a child process if necessary. */
8411 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008412 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008413 jp = makejob(cmd, 1);
8414 if (forkshell(jp, cmd, FORK_FG) != 0) {
8415 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008416 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008417 break;
8418 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008419 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008420 }
8421 listsetvar(varlist.list, VEXPORT|VSTACK);
8422 shellexec(argv, path, cmdentry.u.index);
8423 /* NOTREACHED */
8424
8425 case CMDBUILTIN:
8426 cmdenviron = varlist.list;
8427 if (cmdenviron) {
8428 struct strlist *list = cmdenviron;
8429 int i = VNOSET;
8430 if (spclbltin > 0 || argc == 0) {
8431 i = 0;
8432 if (cmd_is_exec && argc > 1)
8433 i = VEXPORT;
8434 }
8435 listsetvar(list, i);
8436 }
8437 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8438 int exit_status;
8439 int i, j;
8440
8441 i = exception;
8442 if (i == EXEXIT)
8443 goto raise;
8444
8445 exit_status = 2;
8446 j = 0;
8447 if (i == EXINT)
8448 j = SIGINT;
8449 if (i == EXSIG)
8450 j = pendingsigs;
8451 if (j)
8452 exit_status = j + 128;
8453 exitstatus = exit_status;
8454
8455 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008456 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008457 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008458 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008459 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008460 }
8461 break;
8462
8463 case CMDFUNCTION:
8464 listsetvar(varlist.list, 0);
8465 if (evalfun(cmdentry.u.func, argc, argv, flags))
8466 goto raise;
8467 break;
8468 }
8469
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008470 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008471 popredir(cmd_is_exec);
8472 if (lastarg)
8473 /* dsl: I think this is intended to be used to support
8474 * '_' in 'vi' command mode during line editing...
8475 * However I implemented that within libedit itself.
8476 */
8477 setvar("_", lastarg, 0);
8478 popstackmark(&smark);
8479}
8480
8481static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008482evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8483{
Eric Andersenc470f442003-07-28 09:56:35 +00008484 char *volatile savecmdname;
8485 struct jmploc *volatile savehandler;
8486 struct jmploc jmploc;
8487 int i;
8488
8489 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008490 i = setjmp(jmploc.loc);
8491 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008492 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008493 savehandler = exception_handler;
8494 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008495 commandname = argv[0];
8496 argptr = argv + 1;
8497 optptr = NULL; /* initialize nextopt */
8498 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008499 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008500 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008501 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008502 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008503 commandname = savecmdname;
8504 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008505 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008506
8507 return i;
8508}
8509
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008510static int
8511goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008512{
8513 return !*endofname(p);
8514}
8515
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008516
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008517/*
8518 * Search for a command. This is called before we fork so that the
8519 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008520 * the child. The check for "goodname" is an overly conservative
8521 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008522 */
Eric Andersenc470f442003-07-28 09:56:35 +00008523static void
8524prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008525{
8526 struct cmdentry entry;
8527
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008528 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8529 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008530}
8531
Eric Andersencb57d552001-06-28 07:25:16 +00008532
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008533/* ============ Builtin commands
8534 *
8535 * Builtin commands whose functions are closely tied to evaluation
8536 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008537 */
8538
8539/*
Eric Andersencb57d552001-06-28 07:25:16 +00008540 * Handle break and continue commands. Break, continue, and return are
8541 * all handled by setting the evalskip flag. The evaluation routines
8542 * above all check this flag, and if it is set they start skipping
8543 * commands rather than executing them. The variable skipcount is
8544 * the number of loops to break/continue, or the number of function
8545 * levels to return. (The latter is always 1.) It should probably
8546 * be an error to break out of more loops than exist, but it isn't
8547 * in the standard shell so we don't make it one here.
8548 */
Eric Andersenc470f442003-07-28 09:56:35 +00008549static int
8550breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008551{
8552 int n = argc > 1 ? number(argv[1]) : 1;
8553
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008554 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008555 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008556 if (n > loopnest)
8557 n = loopnest;
8558 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008559 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008560 skipcount = n;
8561 }
8562 return 0;
8563}
8564
Eric Andersenc470f442003-07-28 09:56:35 +00008565
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008566/* ============ input.c
8567 *
Eric Andersen90898442003-08-06 11:20:52 +00008568 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008569 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008570
Eric Andersenc470f442003-07-28 09:56:35 +00008571#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008572
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008573enum {
8574 INPUT_PUSH_FILE = 1,
8575 INPUT_NOFILE_OK = 2,
8576};
Eric Andersencb57d552001-06-28 07:25:16 +00008577
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008578/*
8579 * NEOF is returned by parsecmd when it encounters an end of file. It
8580 * must be distinct from NULL, so we use the address of a variable that
8581 * happens to be handy.
8582 */
8583static int plinno = 1; /* input line number */
8584/* number of characters left in input buffer */
8585static int parsenleft; /* copy of parsefile->nleft */
8586static int parselleft; /* copy of parsefile->lleft */
8587/* next character in input buffer */
8588static char *parsenextc; /* copy of parsefile->nextc */
8589
8590static int checkkwd;
8591/* values of checkkwd variable */
8592#define CHKALIAS 0x1
8593#define CHKKWD 0x2
8594#define CHKNL 0x4
8595
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008596static void
8597popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008598{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008599 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008600
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008601 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008602#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008603 if (sp->ap) {
8604 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8605 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008606 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008607 if (sp->string != sp->ap->val) {
8608 free(sp->string);
8609 }
8610 sp->ap->flag &= ~ALIASINUSE;
8611 if (sp->ap->flag & ALIASDEAD) {
8612 unalias(sp->ap->name);
8613 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008614 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008615#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008616 parsenextc = sp->prevstring;
8617 parsenleft = sp->prevnleft;
8618/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8619 parsefile->strpush = sp->prev;
8620 if (sp != &(parsefile->basestrpush))
8621 free(sp);
8622 INT_ON;
8623}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008624
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008625static int
8626preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008627{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008628 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008629 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008630 parsenextc = buf;
8631
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008632 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008633#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008634 if (!iflag || parsefile->fd)
8635 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8636 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008637#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008638 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008639#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008640 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8641 if (nr == 0) {
8642 /* Ctrl+C pressed */
8643 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008644 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008645 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008646 raise(SIGINT);
8647 return 1;
8648 }
Eric Andersenc470f442003-07-28 09:56:35 +00008649 goto retry;
8650 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008651 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008652 /* Ctrl+D presend */
8653 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008654 }
Eric Andersencb57d552001-06-28 07:25:16 +00008655 }
8656#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008657 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008658#endif
8659
8660 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008661 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
8662 int flags = fcntl(0, F_GETFL, 0);
8663 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008664 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008665 if (fcntl(0, F_SETFL, flags) >= 0) {
8666 out2str("sh: turning off NDELAY mode\n");
8667 goto retry;
8668 }
8669 }
8670 }
8671 }
8672 return nr;
8673}
8674
8675/*
8676 * Refill the input buffer and return the next input character:
8677 *
8678 * 1) If a string was pushed back on the input, pop it;
8679 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8680 * from a string so we can't refill the buffer, return EOF.
8681 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8682 * 4) Process input up to the next newline, deleting nul characters.
8683 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008684static int
Eric Andersenc470f442003-07-28 09:56:35 +00008685preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008686{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008687 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008688 int more;
8689 char savec;
8690
8691 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008692#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008693 if (parsenleft == -1 && parsefile->strpush->ap &&
8694 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008695 return PEOA;
8696 }
Eric Andersen2870d962001-07-02 17:27:21 +00008697#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008698 popstring();
8699 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008700 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008701 }
8702 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8703 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008704 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008705
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008706 more = parselleft;
8707 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008708 again:
8709 more = preadfd();
8710 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008711 parselleft = parsenleft = EOF_NLEFT;
8712 return PEOF;
8713 }
8714 }
8715
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008716 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008717
8718 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008719 for (;;) {
8720 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008721
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008722 more--;
8723 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008724
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008725 if (!c)
8726 memmove(q, q + 1, more);
8727 else {
8728 q++;
8729 if (c == '\n') {
8730 parsenleft = q - parsenextc - 1;
8731 break;
8732 }
Eric Andersencb57d552001-06-28 07:25:16 +00008733 }
8734
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008735 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008736 parsenleft = q - parsenextc - 1;
8737 if (parsenleft < 0)
8738 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008739 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008740 }
8741 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008742 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008743
8744 savec = *q;
8745 *q = '\0';
8746
8747 if (vflag) {
8748 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008749 }
8750
8751 *q = savec;
8752
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008753 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008754}
8755
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008756#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008757static int
8758pgetc(void)
8759{
8760 return pgetc_as_macro();
8761}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008762
8763#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8764#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008765#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008766#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008767#endif
8768
8769/*
8770 * Same as pgetc(), but ignores PEOA.
8771 */
8772#if ENABLE_ASH_ALIAS
8773static int
8774pgetc2(void)
8775{
8776 int c;
8777
8778 do {
8779 c = pgetc_macro();
8780 } while (c == PEOA);
8781 return c;
8782}
8783#else
8784static int
8785pgetc2(void)
8786{
8787 return pgetc_macro();
8788}
8789#endif
8790
8791/*
8792 * Read a line from the script.
8793 */
8794static char *
8795pfgets(char *line, int len)
8796{
8797 char *p = line;
8798 int nleft = len;
8799 int c;
8800
8801 while (--nleft > 0) {
8802 c = pgetc2();
8803 if (c == PEOF) {
8804 if (p == line)
8805 return NULL;
8806 break;
8807 }
8808 *p++ = c;
8809 if (c == '\n')
8810 break;
8811 }
8812 *p = '\0';
8813 return line;
8814}
8815
Eric Andersenc470f442003-07-28 09:56:35 +00008816/*
8817 * Undo the last call to pgetc. Only one character may be pushed back.
8818 * PEOF may be pushed back.
8819 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008820static void
Eric Andersenc470f442003-07-28 09:56:35 +00008821pungetc(void)
8822{
8823 parsenleft++;
8824 parsenextc--;
8825}
Eric Andersencb57d552001-06-28 07:25:16 +00008826
8827/*
8828 * Push a string back onto the input at this current parsefile level.
8829 * We handle aliases this way.
8830 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008831static void
Eric Andersenc470f442003-07-28 09:56:35 +00008832pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008833{
Eric Andersencb57d552001-06-28 07:25:16 +00008834 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008835 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008836
Eric Andersenc470f442003-07-28 09:56:35 +00008837 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008838 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008839/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8840 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008841 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008842 sp->prev = parsefile->strpush;
8843 parsefile->strpush = sp;
8844 } else
8845 sp = parsefile->strpush = &(parsefile->basestrpush);
8846 sp->prevstring = parsenextc;
8847 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008848#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008849 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008850 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008851 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008852 sp->string = s;
8853 }
Eric Andersen2870d962001-07-02 17:27:21 +00008854#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008855 parsenextc = s;
8856 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008857 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008858}
8859
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008860/*
8861 * To handle the "." command, a stack of input files is used. Pushfile
8862 * adds a new entry to the stack and popfile restores the previous level.
8863 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008864static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008865pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008866{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008867 struct parsefile *pf;
8868
8869 parsefile->nleft = parsenleft;
8870 parsefile->lleft = parselleft;
8871 parsefile->nextc = parsenextc;
8872 parsefile->linno = plinno;
8873 pf = ckmalloc(sizeof(*pf));
8874 pf->prev = parsefile;
8875 pf->fd = -1;
8876 pf->strpush = NULL;
8877 pf->basestrpush.prev = NULL;
8878 parsefile = pf;
8879}
8880
8881static void
8882popfile(void)
8883{
8884 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008885
Denis Vlasenkob012b102007-02-19 22:43:01 +00008886 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008887 if (pf->fd >= 0)
8888 close(pf->fd);
8889 if (pf->buf)
8890 free(pf->buf);
8891 while (pf->strpush)
8892 popstring();
8893 parsefile = pf->prev;
8894 free(pf);
8895 parsenleft = parsefile->nleft;
8896 parselleft = parsefile->lleft;
8897 parsenextc = parsefile->nextc;
8898 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008899 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008900}
8901
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008902/*
8903 * Return to top level.
8904 */
8905static void
8906popallfiles(void)
8907{
8908 while (parsefile != &basepf)
8909 popfile();
8910}
8911
8912/*
8913 * Close the file(s) that the shell is reading commands from. Called
8914 * after a fork is done.
8915 */
8916static void
8917closescript(void)
8918{
8919 popallfiles();
8920 if (parsefile->fd > 0) {
8921 close(parsefile->fd);
8922 parsefile->fd = 0;
8923 }
8924}
8925
8926/*
8927 * Like setinputfile, but takes an open file descriptor. Call this with
8928 * interrupts off.
8929 */
8930static void
8931setinputfd(int fd, int push)
8932{
8933 fcntl(fd, F_SETFD, FD_CLOEXEC);
8934 if (push) {
8935 pushfile();
8936 parsefile->buf = 0;
8937 }
8938 parsefile->fd = fd;
8939 if (parsefile->buf == NULL)
8940 parsefile->buf = ckmalloc(IBUFSIZ);
8941 parselleft = parsenleft = 0;
8942 plinno = 1;
8943}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008944
Eric Andersenc470f442003-07-28 09:56:35 +00008945/*
8946 * Set the input to take input from a file. If push is set, push the
8947 * old input onto the stack first.
8948 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008949static int
8950setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008951{
8952 int fd;
8953 int fd2;
8954
Denis Vlasenkob012b102007-02-19 22:43:01 +00008955 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008956 fd = open(fname, O_RDONLY);
8957 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008958 if (flags & INPUT_NOFILE_OK)
8959 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008960 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008961 }
Eric Andersenc470f442003-07-28 09:56:35 +00008962 if (fd < 10) {
8963 fd2 = copyfd(fd, 10);
8964 close(fd);
8965 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008966 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008967 fd = fd2;
8968 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008969 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008970 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008971 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008972 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008973}
8974
Eric Andersencb57d552001-06-28 07:25:16 +00008975/*
8976 * Like setinputfile, but takes input from a string.
8977 */
Eric Andersenc470f442003-07-28 09:56:35 +00008978static void
8979setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008980{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008981 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008982 pushfile();
8983 parsenextc = string;
8984 parsenleft = strlen(string);
8985 parsefile->buf = NULL;
8986 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008987 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008988}
8989
8990
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008991/* ============ mail.c
8992 *
8993 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008994 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008995
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008996#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008997
Eric Andersencb57d552001-06-28 07:25:16 +00008998#define MAXMBOXES 10
8999
Eric Andersenc470f442003-07-28 09:56:35 +00009000/* times of mailboxes */
9001static time_t mailtime[MAXMBOXES];
9002/* Set if MAIL or MAILPATH is changed. */
9003static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009004
Eric Andersencb57d552001-06-28 07:25:16 +00009005/*
Eric Andersenc470f442003-07-28 09:56:35 +00009006 * Print appropriate message(s) if mail has arrived.
9007 * If mail_var_path_changed is set,
9008 * then the value of MAIL has mail_var_path_changed,
9009 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009010 */
Eric Andersenc470f442003-07-28 09:56:35 +00009011static void
9012chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009013{
Eric Andersencb57d552001-06-28 07:25:16 +00009014 const char *mpath;
9015 char *p;
9016 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009017 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009018 struct stackmark smark;
9019 struct stat statb;
9020
Eric Andersencb57d552001-06-28 07:25:16 +00009021 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009022 mpath = mpathset() ? mpathval() : mailval();
9023 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009024 p = padvance(&mpath, nullstr);
9025 if (p == NULL)
9026 break;
9027 if (*p == '\0')
9028 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009029 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009030#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009031 if (q[-1] != '/')
9032 abort();
9033#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009034 q[-1] = '\0'; /* delete trailing '/' */
9035 if (stat(p, &statb) < 0) {
9036 *mtp = 0;
9037 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009038 }
Eric Andersenc470f442003-07-28 09:56:35 +00009039 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9040 fprintf(
9041 stderr, snlfmt,
9042 pathopt ? pathopt : "you have mail"
9043 );
9044 }
9045 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009046 }
Eric Andersenc470f442003-07-28 09:56:35 +00009047 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009048 popstackmark(&smark);
9049}
Eric Andersencb57d552001-06-28 07:25:16 +00009050
Eric Andersenc470f442003-07-28 09:56:35 +00009051static void
9052changemail(const char *val)
9053{
9054 mail_var_path_changed++;
9055}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009056
Denis Vlasenko131ae172007-02-18 13:00:19 +00009057#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009058
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009059
9060/* ============ ??? */
9061
Eric Andersencb57d552001-06-28 07:25:16 +00009062/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009063 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009064 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009065static void
9066setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009067{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009068 char **newparam;
9069 char **ap;
9070 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009071
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009072 for (nparam = 0; argv[nparam]; nparam++);
9073 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9074 while (*argv) {
9075 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009076 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009077 *ap = NULL;
9078 freeparam(&shellparam);
9079 shellparam.malloc = 1;
9080 shellparam.nparam = nparam;
9081 shellparam.p = newparam;
9082#if ENABLE_ASH_GETOPTS
9083 shellparam.optind = 1;
9084 shellparam.optoff = -1;
9085#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009086}
9087
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009088/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009089 * Process shell options. The global variable argptr contains a pointer
9090 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009091 */
9092static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009093minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009094{
9095 int i;
9096
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009097 if (name) {
9098 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009099 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009100 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009101 return;
9102 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009103 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009104 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009105 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009106 out1str("Current option settings\n");
9107 for (i = 0; i < NOPTS; i++)
9108 out1fmt("%-16s%s\n", optnames(i),
9109 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009110}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009111static void
9112setoption(int flag, int val)
9113{
9114 int i;
9115
9116 for (i = 0; i < NOPTS; i++) {
9117 if (optletters(i) == flag) {
9118 optlist[i] = val;
9119 return;
9120 }
9121 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009122 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009123 /* NOTREACHED */
9124}
Eric Andersenc470f442003-07-28 09:56:35 +00009125static void
9126options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009127{
9128 char *p;
9129 int val;
9130 int c;
9131
9132 if (cmdline)
9133 minusc = NULL;
9134 while ((p = *argptr) != NULL) {
9135 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009136 c = *p++;
9137 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009138 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009139 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009140 if (!cmdline) {
9141 /* "-" means turn off -x and -v */
9142 if (p[0] == '\0')
9143 xflag = vflag = 0;
9144 /* "--" means reset params */
9145 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009146 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009147 }
Eric Andersenc470f442003-07-28 09:56:35 +00009148 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009149 }
9150 } else if (c == '+') {
9151 val = 0;
9152 } else {
9153 argptr--;
9154 break;
9155 }
9156 while ((c = *p++) != '\0') {
9157 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00009158 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00009159 } else if (c == 'o') {
9160 minus_o(*argptr, val);
9161 if (*argptr)
9162 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00009163 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009164 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009165 isloginsh = 1;
9166 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009167 } else {
9168 setoption(c, val);
9169 }
9170 }
9171 }
9172}
9173
Eric Andersencb57d552001-06-28 07:25:16 +00009174/*
Eric Andersencb57d552001-06-28 07:25:16 +00009175 * The shift builtin command.
9176 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009177static int
Eric Andersenc470f442003-07-28 09:56:35 +00009178shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009179{
9180 int n;
9181 char **ap1, **ap2;
9182
9183 n = 1;
9184 if (argc > 1)
9185 n = number(argv[1]);
9186 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009187 ash_msg_and_raise_error("can't shift that many");
9188 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009189 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009190 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009191 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009192 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009193 }
9194 ap2 = shellparam.p;
9195 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009196#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009197 shellparam.optind = 1;
9198 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009199#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009200 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009201 return 0;
9202}
9203
Eric Andersencb57d552001-06-28 07:25:16 +00009204/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009205 * POSIX requires that 'set' (but not export or readonly) output the
9206 * variables in lexicographic order - by the locale's collating order (sigh).
9207 * Maybe we could keep them in an ordered balanced binary tree
9208 * instead of hashed lists.
9209 * For now just roll 'em through qsort for printing...
9210 */
9211static int
9212showvars(const char *sep_prefix, int on, int off)
9213{
9214 const char *sep;
9215 char **ep, **epend;
9216
9217 ep = listvars(on, off, &epend);
9218 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9219
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009220 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009221
9222 for (; ep < epend; ep++) {
9223 const char *p;
9224 const char *q;
9225
9226 p = strchrnul(*ep, '=');
9227 q = nullstr;
9228 if (*p)
9229 q = single_quote(++p);
9230 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9231 }
9232 return 0;
9233}
9234
9235/*
Eric Andersencb57d552001-06-28 07:25:16 +00009236 * The set command builtin.
9237 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009238static int
Eric Andersenc470f442003-07-28 09:56:35 +00009239setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009240{
9241 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009242 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009243 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009244 options(0);
9245 optschanged();
9246 if (*argptr != NULL) {
9247 setparam(argptr);
9248 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009249 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009250 return 0;
9251}
9252
Denis Vlasenko131ae172007-02-18 13:00:19 +00009253#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009254/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009255static void
9256change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009257{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009258 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009259 /* "get", generate */
9260 char buf[16];
9261
9262 rseed = rseed * 1103515245 + 12345;
9263 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9264 /* set without recursion */
9265 setvar(vrandom.text, buf, VNOFUNC);
9266 vrandom.flags &= ~VNOFUNC;
9267 } else {
9268 /* set/reset */
9269 rseed = strtoul(value, (char **)NULL, 10);
9270 }
Eric Andersenef02f822004-03-11 13:34:24 +00009271}
Eric Andersen16767e22004-03-16 05:14:10 +00009272#endif
9273
Denis Vlasenko131ae172007-02-18 13:00:19 +00009274#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009275static int
Eric Andersenc470f442003-07-28 09:56:35 +00009276getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009277{
9278 char *p, *q;
9279 char c = '?';
9280 int done = 0;
9281 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009282 char s[12];
9283 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009284
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009285 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009286 return 1;
9287 optnext = optfirst + *param_optind - 1;
9288
9289 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009290 p = NULL;
9291 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009292 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009293 if (p == NULL || *p == '\0') {
9294 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009295 p = *optnext;
9296 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009297 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009298 p = NULL;
9299 done = 1;
9300 goto out;
9301 }
9302 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009303 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009304 goto atend;
9305 }
9306
9307 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009308 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009309 if (*q == '\0') {
9310 if (optstr[0] == ':') {
9311 s[0] = c;
9312 s[1] = '\0';
9313 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009314 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009315 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009316 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009317 }
9318 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009319 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009320 }
9321 if (*++q == ':')
9322 q++;
9323 }
9324
9325 if (*++q == ':') {
9326 if (*p == '\0' && (p = *optnext) == NULL) {
9327 if (optstr[0] == ':') {
9328 s[0] = c;
9329 s[1] = '\0';
9330 err |= setvarsafe("OPTARG", s, 0);
9331 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009332 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009333 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009334 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009335 c = '?';
9336 }
Eric Andersenc470f442003-07-28 09:56:35 +00009337 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009338 }
9339
9340 if (p == *optnext)
9341 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009342 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009343 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009344 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009345 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009346 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009347 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009348 *param_optind = optnext - optfirst + 1;
9349 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009350 err |= setvarsafe("OPTIND", s, VNOFUNC);
9351 s[0] = c;
9352 s[1] = '\0';
9353 err |= setvarsafe(optvar, s, 0);
9354 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009355 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009356 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009357 flush_stdout_stderr();
9358 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009359 }
9360 return done;
9361}
Eric Andersenc470f442003-07-28 09:56:35 +00009362
9363/*
9364 * The getopts builtin. Shellparam.optnext points to the next argument
9365 * to be processed. Shellparam.optptr points to the next character to
9366 * be processed in the current argument. If shellparam.optnext is NULL,
9367 * then it's the first time getopts has been called.
9368 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009369static int
Eric Andersenc470f442003-07-28 09:56:35 +00009370getoptscmd(int argc, char **argv)
9371{
9372 char **optbase;
9373
9374 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009375 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009376 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009377 optbase = shellparam.p;
9378 if (shellparam.optind > shellparam.nparam + 1) {
9379 shellparam.optind = 1;
9380 shellparam.optoff = -1;
9381 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009382 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009383 optbase = &argv[3];
9384 if (shellparam.optind > argc - 2) {
9385 shellparam.optind = 1;
9386 shellparam.optoff = -1;
9387 }
9388 }
9389
9390 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009391 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009392}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009393#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009394
Eric Andersencb57d552001-06-28 07:25:16 +00009395
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009396/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009397
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009398static int tokpushback; /* last token pushed back */
9399#define NEOF ((union node *)&tokpushback)
9400static int parsebackquote; /* nonzero if we are inside backquotes */
9401static int lasttoken; /* last token read */
9402static char *wordtext; /* text of last word returned by readtoken */
9403static struct nodelist *backquotelist;
9404static union node *redirnode;
9405static struct heredoc *heredoc;
9406static int quoteflag; /* set if (part of) last token was quoted */
9407
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009408static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9409static void
9410raise_error_syntax(const char *msg)
9411{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009412 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009413 /* NOTREACHED */
9414}
9415
9416/*
9417 * Called when an unexpected token is read during the parse. The argument
9418 * is the token that is expected, or -1 if more than one type of token can
9419 * occur at this point.
9420 */
9421static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9422static void
9423raise_error_unexpected_syntax(int token)
9424{
9425 char msg[64];
9426 int l;
9427
9428 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9429 if (token >= 0)
9430 sprintf(msg + l, " (expecting %s)", tokname(token));
9431 raise_error_syntax(msg);
9432 /* NOTREACHED */
9433}
Eric Andersencb57d552001-06-28 07:25:16 +00009434
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009435#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009436
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009437struct heredoc {
9438 struct heredoc *next; /* next here document in list */
9439 union node *here; /* redirection node */
9440 char *eofmark; /* string indicating end of input */
9441 int striptabs; /* if set, strip leading tabs */
9442};
Eric Andersencb57d552001-06-28 07:25:16 +00009443
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009444static struct heredoc *heredoclist; /* list of here documents to read */
9445
9446/* parsing is heavily cross-recursive, need these forward decls */
9447static union node *andor(void);
9448static union node *pipeline(void);
9449static union node *parse_command(void);
9450static void parseheredoc(void);
9451static char peektoken(void);
9452static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009453
Eric Andersenc470f442003-07-28 09:56:35 +00009454static union node *
9455list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009456{
9457 union node *n1, *n2, *n3;
9458 int tok;
9459
Eric Andersenc470f442003-07-28 09:56:35 +00009460 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9461 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009462 return NULL;
9463 n1 = NULL;
9464 for (;;) {
9465 n2 = andor();
9466 tok = readtoken();
9467 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009468 if (n2->type == NPIPE) {
9469 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009470 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009471 if (n2->type != NREDIR) {
9472 n3 = stalloc(sizeof(struct nredir));
9473 n3->nredir.n = n2;
9474 n3->nredir.redirect = NULL;
9475 n2 = n3;
9476 }
9477 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009478 }
9479 }
9480 if (n1 == NULL) {
9481 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009482 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009483 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009484 n3->type = NSEMI;
9485 n3->nbinary.ch1 = n1;
9486 n3->nbinary.ch2 = n2;
9487 n1 = n3;
9488 }
9489 switch (tok) {
9490 case TBACKGND:
9491 case TSEMI:
9492 tok = readtoken();
9493 /* fall through */
9494 case TNL:
9495 if (tok == TNL) {
9496 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009497 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009498 return n1;
9499 } else {
9500 tokpushback++;
9501 }
Eric Andersenc470f442003-07-28 09:56:35 +00009502 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009503 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009504 return n1;
9505 break;
9506 case TEOF:
9507 if (heredoclist)
9508 parseheredoc();
9509 else
Eric Andersenc470f442003-07-28 09:56:35 +00009510 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009511 return n1;
9512 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009513 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009514 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009515 tokpushback++;
9516 return n1;
9517 }
9518 }
9519}
9520
Eric Andersenc470f442003-07-28 09:56:35 +00009521static union node *
9522andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009523{
Eric Andersencb57d552001-06-28 07:25:16 +00009524 union node *n1, *n2, *n3;
9525 int t;
9526
Eric Andersencb57d552001-06-28 07:25:16 +00009527 n1 = pipeline();
9528 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009529 t = readtoken();
9530 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009531 t = NAND;
9532 } else if (t == TOR) {
9533 t = NOR;
9534 } else {
9535 tokpushback++;
9536 return n1;
9537 }
Eric Andersenc470f442003-07-28 09:56:35 +00009538 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009539 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009540 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009541 n3->type = t;
9542 n3->nbinary.ch1 = n1;
9543 n3->nbinary.ch2 = n2;
9544 n1 = n3;
9545 }
9546}
9547
Eric Andersenc470f442003-07-28 09:56:35 +00009548static union node *
9549pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009550{
Eric Andersencb57d552001-06-28 07:25:16 +00009551 union node *n1, *n2, *pipenode;
9552 struct nodelist *lp, *prev;
9553 int negate;
9554
9555 negate = 0;
9556 TRACE(("pipeline: entered\n"));
9557 if (readtoken() == TNOT) {
9558 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009559 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009560 } else
9561 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009562 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009563 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009564 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009565 pipenode->type = NPIPE;
9566 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009567 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009568 pipenode->npipe.cmdlist = lp;
9569 lp->n = n1;
9570 do {
9571 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009572 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009573 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009574 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009575 prev->next = lp;
9576 } while (readtoken() == TPIPE);
9577 lp->next = NULL;
9578 n1 = pipenode;
9579 }
9580 tokpushback++;
9581 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009582 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009583 n2->type = NNOT;
9584 n2->nnot.com = n1;
9585 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009586 }
9587 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009588}
9589
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009590static union node *
9591makename(void)
9592{
9593 union node *n;
9594
9595 n = stalloc(sizeof(struct narg));
9596 n->type = NARG;
9597 n->narg.next = NULL;
9598 n->narg.text = wordtext;
9599 n->narg.backquote = backquotelist;
9600 return n;
9601}
9602
9603static void
9604fixredir(union node *n, const char *text, int err)
9605{
9606 TRACE(("Fix redir %s %d\n", text, err));
9607 if (!err)
9608 n->ndup.vname = NULL;
9609
9610 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009611 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009612 else if (LONE_DASH(text))
9613 n->ndup.dupfd = -1;
9614 else {
9615 if (err)
9616 raise_error_syntax("Bad fd number");
9617 n->ndup.vname = makename();
9618 }
9619}
9620
9621/*
9622 * Returns true if the text contains nothing to expand (no dollar signs
9623 * or backquotes).
9624 */
9625static int
9626noexpand(char *text)
9627{
9628 char *p;
9629 char c;
9630
9631 p = text;
9632 while ((c = *p++) != '\0') {
9633 if (c == CTLQUOTEMARK)
9634 continue;
9635 if (c == CTLESC)
9636 p++;
9637 else if (SIT(c, BASESYNTAX) == CCTL)
9638 return 0;
9639 }
9640 return 1;
9641}
9642
9643static void
9644parsefname(void)
9645{
9646 union node *n = redirnode;
9647
9648 if (readtoken() != TWORD)
9649 raise_error_unexpected_syntax(-1);
9650 if (n->type == NHERE) {
9651 struct heredoc *here = heredoc;
9652 struct heredoc *p;
9653 int i;
9654
9655 if (quoteflag == 0)
9656 n->type = NXHERE;
9657 TRACE(("Here document %d\n", n->type));
9658 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9659 raise_error_syntax("Illegal eof marker for << redirection");
9660 rmescapes(wordtext);
9661 here->eofmark = wordtext;
9662 here->next = NULL;
9663 if (heredoclist == NULL)
9664 heredoclist = here;
9665 else {
9666 for (p = heredoclist; p->next; p = p->next);
9667 p->next = here;
9668 }
9669 } else if (n->type == NTOFD || n->type == NFROMFD) {
9670 fixredir(n, wordtext, 0);
9671 } else {
9672 n->nfile.fname = makename();
9673 }
9674}
Eric Andersencb57d552001-06-28 07:25:16 +00009675
Eric Andersenc470f442003-07-28 09:56:35 +00009676static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009677simplecmd(void)
9678{
9679 union node *args, **app;
9680 union node *n = NULL;
9681 union node *vars, **vpp;
9682 union node **rpp, *redir;
9683 int savecheckkwd;
9684
9685 args = NULL;
9686 app = &args;
9687 vars = NULL;
9688 vpp = &vars;
9689 redir = NULL;
9690 rpp = &redir;
9691
9692 savecheckkwd = CHKALIAS;
9693 for (;;) {
9694 checkkwd = savecheckkwd;
9695 switch (readtoken()) {
9696 case TWORD:
9697 n = stalloc(sizeof(struct narg));
9698 n->type = NARG;
9699 n->narg.text = wordtext;
9700 n->narg.backquote = backquotelist;
9701 if (savecheckkwd && isassignment(wordtext)) {
9702 *vpp = n;
9703 vpp = &n->narg.next;
9704 } else {
9705 *app = n;
9706 app = &n->narg.next;
9707 savecheckkwd = 0;
9708 }
9709 break;
9710 case TREDIR:
9711 *rpp = n = redirnode;
9712 rpp = &n->nfile.next;
9713 parsefname(); /* read name of redirection file */
9714 break;
9715 case TLP:
9716 if (args && app == &args->narg.next
9717 && !vars && !redir
9718 ) {
9719 struct builtincmd *bcmd;
9720 const char *name;
9721
9722 /* We have a function */
9723 if (readtoken() != TRP)
9724 raise_error_unexpected_syntax(TRP);
9725 name = n->narg.text;
9726 if (!goodname(name)
9727 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9728 ) {
9729 raise_error_syntax("Bad function name");
9730 }
9731 n->type = NDEFUN;
9732 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9733 n->narg.next = parse_command();
9734 return n;
9735 }
9736 /* fall through */
9737 default:
9738 tokpushback++;
9739 goto out;
9740 }
9741 }
9742 out:
9743 *app = NULL;
9744 *vpp = NULL;
9745 *rpp = NULL;
9746 n = stalloc(sizeof(struct ncmd));
9747 n->type = NCMD;
9748 n->ncmd.args = args;
9749 n->ncmd.assign = vars;
9750 n->ncmd.redirect = redir;
9751 return n;
9752}
9753
9754static union node *
9755parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009756{
Eric Andersencb57d552001-06-28 07:25:16 +00009757 union node *n1, *n2;
9758 union node *ap, **app;
9759 union node *cp, **cpp;
9760 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009761 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009762 int t;
9763
9764 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009765 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009766
Eric Andersencb57d552001-06-28 07:25:16 +00009767 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009768 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009769 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009770 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009771 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009772 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009773 n1->type = NIF;
9774 n1->nif.test = list(0);
9775 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009776 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009777 n1->nif.ifpart = list(0);
9778 n2 = n1;
9779 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009780 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009781 n2 = n2->nif.elsepart;
9782 n2->type = NIF;
9783 n2->nif.test = list(0);
9784 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009785 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009786 n2->nif.ifpart = list(0);
9787 }
9788 if (lasttoken == TELSE)
9789 n2->nif.elsepart = list(0);
9790 else {
9791 n2->nif.elsepart = NULL;
9792 tokpushback++;
9793 }
Eric Andersenc470f442003-07-28 09:56:35 +00009794 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009795 break;
9796 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009797 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009798 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009799 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009800 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009801 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009802 got = readtoken();
9803 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009804 TRACE(("expecting DO got %s %s\n", tokname(got),
9805 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009806 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009807 }
9808 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009809 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009810 break;
9811 }
9812 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009813 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009814 raise_error_syntax("Bad for loop variable");
9815 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009816 n1->type = NFOR;
9817 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009818 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009819 if (readtoken() == TIN) {
9820 app = &ap;
9821 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009822 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009823 n2->type = NARG;
9824 n2->narg.text = wordtext;
9825 n2->narg.backquote = backquotelist;
9826 *app = n2;
9827 app = &n2->narg.next;
9828 }
9829 *app = NULL;
9830 n1->nfor.args = ap;
9831 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009832 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009833 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009834 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009835 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009836 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009837 n2->narg.backquote = NULL;
9838 n2->narg.next = NULL;
9839 n1->nfor.args = n2;
9840 /*
9841 * Newline or semicolon here is optional (but note
9842 * that the original Bourne shell only allowed NL).
9843 */
9844 if (lasttoken != TNL && lasttoken != TSEMI)
9845 tokpushback++;
9846 }
Eric Andersenc470f442003-07-28 09:56:35 +00009847 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009848 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009849 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009850 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009851 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009852 break;
9853 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009854 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009855 n1->type = NCASE;
9856 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009857 raise_error_unexpected_syntax(TWORD);
9858 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009859 n2->type = NARG;
9860 n2->narg.text = wordtext;
9861 n2->narg.backquote = backquotelist;
9862 n2->narg.next = NULL;
9863 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009864 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009865 } while (readtoken() == TNL);
9866 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009867 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009868 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009869 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009870 checkkwd = CHKNL | CHKKWD;
9871 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009872 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009873 if (lasttoken == TLP)
9874 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009875 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009876 cp->type = NCLIST;
9877 app = &cp->nclist.pattern;
9878 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009879 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009880 ap->type = NARG;
9881 ap->narg.text = wordtext;
9882 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009883 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009884 break;
9885 app = &ap->narg.next;
9886 readtoken();
9887 }
9888 ap->narg.next = NULL;
9889 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009890 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009891 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009892
Eric Andersenc470f442003-07-28 09:56:35 +00009893 cpp = &cp->nclist.next;
9894
9895 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009896 t = readtoken();
9897 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009898 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009899 raise_error_unexpected_syntax(TENDCASE);
9900 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009901 }
Eric Andersenc470f442003-07-28 09:56:35 +00009902 }
Eric Andersencb57d552001-06-28 07:25:16 +00009903 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009904 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009905 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009906 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009907 n1->type = NSUBSHELL;
9908 n1->nredir.n = list(0);
9909 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009910 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009911 break;
9912 case TBEGIN:
9913 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009914 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009915 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009916 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009917 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009918 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009919 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009920 }
9921
Eric Andersenc470f442003-07-28 09:56:35 +00009922 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009923 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009924
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009925 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009926 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009927 checkkwd = CHKKWD | CHKALIAS;
9928 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009929 while (readtoken() == TREDIR) {
9930 *rpp = n2 = redirnode;
9931 rpp = &n2->nfile.next;
9932 parsefname();
9933 }
9934 tokpushback++;
9935 *rpp = NULL;
9936 if (redir) {
9937 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009938 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009939 n2->type = NREDIR;
9940 n2->nredir.n = n1;
9941 n1 = n2;
9942 }
9943 n1->nredir.redirect = redir;
9944 }
Eric Andersencb57d552001-06-28 07:25:16 +00009945 return n1;
9946}
9947
Eric Andersencb57d552001-06-28 07:25:16 +00009948/*
9949 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9950 * is not NULL, read a here document. In the latter case, eofmark is the
9951 * word which marks the end of the document and striptabs is true if
9952 * leading tabs should be stripped from the document. The argument firstc
9953 * is the first character of the input token or document.
9954 *
9955 * Because C does not have internal subroutines, I have simulated them
9956 * using goto's to implement the subroutine linkage. The following macros
9957 * will run code that appears at the end of readtoken1.
9958 */
9959
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009960static int parsebackquote; /* nonzero if we are inside backquotes */
9961
Eric Andersen2870d962001-07-02 17:27:21 +00009962#define CHECKEND() {goto checkend; checkend_return:;}
9963#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9964#define PARSESUB() {goto parsesub; parsesub_return:;}
9965#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9966#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9967#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009968
9969static int
Eric Andersenc470f442003-07-28 09:56:35 +00009970readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009971{
Eric Andersencb57d552001-06-28 07:25:16 +00009972 int c = firstc;
9973 char *out;
9974 int len;
9975 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009976 struct nodelist *bqlist = 0;
9977 int quotef = 0;
9978 int dblquote = 0;
9979 int varnest = 0; /* levels of variables expansion */
9980 int arinest = 0; /* levels of arithmetic expansion */
9981 int parenlevel = 0; /* levels of parens in arithmetic */
9982 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9983 int oldstyle = 0;
9984 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009985#if __GNUC__
9986 /* Avoid longjmp clobbering */
9987 (void) &out;
9988 (void) &quotef;
9989 (void) &dblquote;
9990 (void) &varnest;
9991 (void) &arinest;
9992 (void) &parenlevel;
9993 (void) &dqvarnest;
9994 (void) &oldstyle;
9995 (void) &prevsyntax;
9996 (void) &syntax;
9997#endif
9998
9999 startlinno = plinno;
10000 dblquote = 0;
10001 if (syntax == DQSYNTAX)
10002 dblquote = 1;
10003 quotef = 0;
10004 bqlist = NULL;
10005 varnest = 0;
10006 arinest = 0;
10007 parenlevel = 0;
10008 dqvarnest = 0;
10009
10010 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010011 loop: { /* for each line, until end of word */
10012 CHECKEND(); /* set c to PEOF if at end of here document */
10013 for (;;) { /* until end of line or end of word */
10014 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010015 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010016 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010017 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010018 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010019 USTPUTC(c, out);
10020 plinno++;
10021 if (doprompt)
10022 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010023 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010024 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010025 case CWORD:
10026 USTPUTC(c, out);
10027 break;
10028 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010029 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010030 USTPUTC(CTLESC, out);
10031 USTPUTC(c, out);
10032 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010033 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010034 c = pgetc2();
10035 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010036 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010037 USTPUTC('\\', out);
10038 pungetc();
10039 } else if (c == '\n') {
10040 if (doprompt)
10041 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010042 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010043 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010044 c != '\\' && c != '`' &&
10045 c != '$' && (
10046 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010047 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010048 ) {
10049 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010050 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010051 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010052 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010053 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010054 USTPUTC(c, out);
10055 quotef++;
10056 }
10057 break;
10058 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010059 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010060 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010061 if (eofmark == NULL) {
10062 USTPUTC(CTLQUOTEMARK, out);
10063 }
Eric Andersencb57d552001-06-28 07:25:16 +000010064 break;
10065 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010066 syntax = DQSYNTAX;
10067 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010068 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010069 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010070 if (eofmark != NULL && arinest == 0
10071 && varnest == 0
10072 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010073 USTPUTC(c, out);
10074 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010075 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010076 syntax = BASESYNTAX;
10077 dblquote = 0;
10078 }
10079 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +000010080 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010081 }
10082 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010083 case CVAR: /* '$' */
10084 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010085 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010086 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010087 if (varnest > 0) {
10088 varnest--;
10089 if (dqvarnest > 0) {
10090 dqvarnest--;
10091 }
10092 USTPUTC(CTLENDVAR, out);
10093 } else {
10094 USTPUTC(c, out);
10095 }
10096 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010097#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010098 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010099 parenlevel++;
10100 USTPUTC(c, out);
10101 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010102 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010103 if (parenlevel > 0) {
10104 USTPUTC(c, out);
10105 --parenlevel;
10106 } else {
10107 if (pgetc() == ')') {
10108 if (--arinest == 0) {
10109 USTPUTC(CTLENDARI, out);
10110 syntax = prevsyntax;
10111 if (syntax == DQSYNTAX)
10112 dblquote = 1;
10113 else
10114 dblquote = 0;
10115 } else
10116 USTPUTC(')', out);
10117 } else {
10118 /*
10119 * unbalanced parens
10120 * (don't 2nd guess - no error)
10121 */
10122 pungetc();
10123 USTPUTC(')', out);
10124 }
10125 }
10126 break;
10127#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010128 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010129 PARSEBACKQOLD();
10130 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010131 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010132 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010133 case CIGN:
10134 break;
10135 default:
10136 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010137 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010138#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010139 if (c != PEOA)
10140#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010141 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010142
Eric Andersencb57d552001-06-28 07:25:16 +000010143 }
10144 c = pgetc_macro();
10145 }
10146 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010147 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010148#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010149 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010150 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010151#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010152 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010153 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010154 if (varnest != 0) {
10155 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010156 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010157 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010158 }
10159 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010160 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010161 out = stackblock();
10162 if (eofmark == NULL) {
10163 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010164 && quotef == 0
10165 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010166 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010167 PARSEREDIR();
10168 return lasttoken = TREDIR;
10169 } else {
10170 pungetc();
10171 }
10172 }
10173 quoteflag = quotef;
10174 backquotelist = bqlist;
10175 grabstackblock(len);
10176 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010177 lasttoken = TWORD;
10178 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010179/* end of readtoken routine */
10180
Eric Andersencb57d552001-06-28 07:25:16 +000010181/*
10182 * Check to see whether we are at the end of the here document. When this
10183 * is called, c is set to the first character of the next input line. If
10184 * we are at the end of the here document, this routine sets the c to PEOF.
10185 */
Eric Andersenc470f442003-07-28 09:56:35 +000010186checkend: {
10187 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010188#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010189 if (c == PEOA) {
10190 c = pgetc2();
10191 }
10192#endif
10193 if (striptabs) {
10194 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010195 c = pgetc2();
10196 }
Eric Andersenc470f442003-07-28 09:56:35 +000010197 }
10198 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010199 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010200 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010201
Eric Andersenc470f442003-07-28 09:56:35 +000010202 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010203 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010204 if (*p == '\n' && *q == '\0') {
10205 c = PEOF;
10206 plinno++;
10207 needprompt = doprompt;
10208 } else {
10209 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010210 }
10211 }
10212 }
10213 }
Eric Andersenc470f442003-07-28 09:56:35 +000010214 goto checkend_return;
10215}
Eric Andersencb57d552001-06-28 07:25:16 +000010216
Eric Andersencb57d552001-06-28 07:25:16 +000010217/*
10218 * Parse a redirection operator. The variable "out" points to a string
10219 * specifying the fd to be redirected. The variable "c" contains the
10220 * first character of the redirection operator.
10221 */
Eric Andersenc470f442003-07-28 09:56:35 +000010222parseredir: {
10223 char fd = *out;
10224 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010225
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010226 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010227 if (c == '>') {
10228 np->nfile.fd = 1;
10229 c = pgetc();
10230 if (c == '>')
10231 np->type = NAPPEND;
10232 else if (c == '|')
10233 np->type = NCLOBBER;
10234 else if (c == '&')
10235 np->type = NTOFD;
10236 else {
10237 np->type = NTO;
10238 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010239 }
Eric Andersenc470f442003-07-28 09:56:35 +000010240 } else { /* c == '<' */
10241 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010242 c = pgetc();
10243 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010244 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010245 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010246 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010247 np->nfile.fd = 0;
10248 }
10249 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010250 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010251 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010252 c = pgetc();
10253 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010254 heredoc->striptabs = 1;
10255 } else {
10256 heredoc->striptabs = 0;
10257 pungetc();
10258 }
10259 break;
10260
10261 case '&':
10262 np->type = NFROMFD;
10263 break;
10264
10265 case '>':
10266 np->type = NFROMTO;
10267 break;
10268
10269 default:
10270 np->type = NFROM;
10271 pungetc();
10272 break;
10273 }
Eric Andersencb57d552001-06-28 07:25:16 +000010274 }
Eric Andersenc470f442003-07-28 09:56:35 +000010275 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010276 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010277 redirnode = np;
10278 goto parseredir_return;
10279}
Eric Andersencb57d552001-06-28 07:25:16 +000010280
Eric Andersencb57d552001-06-28 07:25:16 +000010281/*
10282 * Parse a substitution. At this point, we have read the dollar sign
10283 * and nothing else.
10284 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010285
10286/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10287 * (assuming ascii char codes, as the original implementation did) */
10288#define is_special(c) \
10289 ((((unsigned int)c) - 33 < 32) \
10290 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010291parsesub: {
10292 int subtype;
10293 int typeloc;
10294 int flags;
10295 char *p;
10296 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010297
Eric Andersenc470f442003-07-28 09:56:35 +000010298 c = pgetc();
10299 if (
10300 c <= PEOA_OR_PEOF ||
10301 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10302 ) {
10303 USTPUTC('$', out);
10304 pungetc();
10305 } else if (c == '(') { /* $(command) or $((arith)) */
10306 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010307#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010308 PARSEARITH();
10309#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010310 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010311#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010312 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010313 pungetc();
10314 PARSEBACKQNEW();
10315 }
10316 } else {
10317 USTPUTC(CTLVAR, out);
10318 typeloc = out - (char *)stackblock();
10319 USTPUTC(VSNORMAL, out);
10320 subtype = VSNORMAL;
10321 if (c == '{') {
10322 c = pgetc();
10323 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010324 c = pgetc();
10325 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010326 c = '#';
10327 else
10328 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010329 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010330 subtype = 0;
10331 }
10332 if (c > PEOA_OR_PEOF && is_name(c)) {
10333 do {
10334 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010335 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010336 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010337 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010338 do {
10339 STPUTC(c, out);
10340 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010341 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010342 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010343 USTPUTC(c, out);
10344 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010345 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010346 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010347
Eric Andersenc470f442003-07-28 09:56:35 +000010348 STPUTC('=', out);
10349 flags = 0;
10350 if (subtype == 0) {
10351 switch (c) {
10352 case ':':
10353 flags = VSNUL;
10354 c = pgetc();
10355 /*FALLTHROUGH*/
10356 default:
10357 p = strchr(types, c);
10358 if (p == NULL)
10359 goto badsub;
10360 subtype = p - types + VSNORMAL;
10361 break;
10362 case '%':
10363 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010364 {
10365 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010366 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010367 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010368 c = pgetc();
10369 if (c == cc)
10370 subtype++;
10371 else
10372 pungetc();
10373 break;
10374 }
10375 }
Eric Andersenc470f442003-07-28 09:56:35 +000010376 } else {
10377 pungetc();
10378 }
10379 if (dblquote || arinest)
10380 flags |= VSQUOTE;
10381 *((char *)stackblock() + typeloc) = subtype | flags;
10382 if (subtype != VSNORMAL) {
10383 varnest++;
10384 if (dblquote || arinest) {
10385 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010386 }
10387 }
10388 }
Eric Andersenc470f442003-07-28 09:56:35 +000010389 goto parsesub_return;
10390}
Eric Andersencb57d552001-06-28 07:25:16 +000010391
Eric Andersencb57d552001-06-28 07:25:16 +000010392/*
10393 * Called to parse command substitutions. Newstyle is set if the command
10394 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10395 * list of commands (passed by reference), and savelen is the number of
10396 * characters on the top of the stack which must be preserved.
10397 */
Eric Andersenc470f442003-07-28 09:56:35 +000010398parsebackq: {
10399 struct nodelist **nlpp;
10400 int savepbq;
10401 union node *n;
10402 char *volatile str;
10403 struct jmploc jmploc;
10404 struct jmploc *volatile savehandler;
10405 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010406 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010407#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010408 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010409#endif
10410
Eric Andersenc470f442003-07-28 09:56:35 +000010411 savepbq = parsebackquote;
10412 if (setjmp(jmploc.loc)) {
10413 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010414 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010415 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010416 exception_handler = savehandler;
10417 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010418 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010419 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010420 str = NULL;
10421 savelen = out - (char *)stackblock();
10422 if (savelen > 0) {
10423 str = ckmalloc(savelen);
10424 memcpy(str, stackblock(), savelen);
10425 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010426 savehandler = exception_handler;
10427 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010428 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010429 if (oldstyle) {
10430 /* We must read until the closing backquote, giving special
10431 treatment to some slashes, and then push the string and
10432 reread it as input, interpreting it normally. */
10433 char *pout;
10434 int pc;
10435 size_t psavelen;
10436 char *pstr;
10437
10438
10439 STARTSTACKSTR(pout);
10440 for (;;) {
10441 if (needprompt) {
10442 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010443 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010444 pc = pgetc();
10445 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010446 case '`':
10447 goto done;
10448
10449 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010450 pc = pgetc();
10451 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010452 plinno++;
10453 if (doprompt)
10454 setprompt(2);
10455 /*
10456 * If eating a newline, avoid putting
10457 * the newline into the new character
10458 * stream (via the STPUTC after the
10459 * switch).
10460 */
10461 continue;
10462 }
10463 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010464 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010465 STPUTC('\\', pout);
10466 if (pc > PEOA_OR_PEOF) {
10467 break;
10468 }
10469 /* fall through */
10470
10471 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010472#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010473 case PEOA:
10474#endif
10475 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010476 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010477
10478 case '\n':
10479 plinno++;
10480 needprompt = doprompt;
10481 break;
10482
10483 default:
10484 break;
10485 }
10486 STPUTC(pc, pout);
10487 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010488 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010489 STPUTC('\0', pout);
10490 psavelen = pout - (char *)stackblock();
10491 if (psavelen > 0) {
10492 pstr = grabstackstr(pout);
10493 setinputstring(pstr);
10494 }
10495 }
10496 nlpp = &bqlist;
10497 while (*nlpp)
10498 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010499 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010500 (*nlpp)->next = NULL;
10501 parsebackquote = oldstyle;
10502
10503 if (oldstyle) {
10504 saveprompt = doprompt;
10505 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010506 }
10507
Eric Andersenc470f442003-07-28 09:56:35 +000010508 n = list(2);
10509
10510 if (oldstyle)
10511 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010512 else if (readtoken() != TRP)
10513 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010514
10515 (*nlpp)->n = n;
10516 if (oldstyle) {
10517 /*
10518 * Start reading from old file again, ignoring any pushed back
10519 * tokens left from the backquote parsing
10520 */
10521 popfile();
10522 tokpushback = 0;
10523 }
10524 while (stackblocksize() <= savelen)
10525 growstackblock();
10526 STARTSTACKSTR(out);
10527 if (str) {
10528 memcpy(out, str, savelen);
10529 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010530 INT_OFF;
10531 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010532 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010533 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010534 }
10535 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010536 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010537 if (arinest || dblquote)
10538 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10539 else
10540 USTPUTC(CTLBACKQ, out);
10541 if (oldstyle)
10542 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010543 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010544}
10545
Denis Vlasenko131ae172007-02-18 13:00:19 +000010546#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010547/*
10548 * Parse an arithmetic expansion (indicate start of one and set state)
10549 */
Eric Andersenc470f442003-07-28 09:56:35 +000010550parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010551 if (++arinest == 1) {
10552 prevsyntax = syntax;
10553 syntax = ARISYNTAX;
10554 USTPUTC(CTLARI, out);
10555 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010556 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010557 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010558 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010559 } else {
10560 /*
10561 * we collapse embedded arithmetic expansion to
10562 * parenthesis, which should be equivalent
10563 */
10564 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010565 }
Eric Andersenc470f442003-07-28 09:56:35 +000010566 goto parsearith_return;
10567}
10568#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010569
Eric Andersenc470f442003-07-28 09:56:35 +000010570} /* end of readtoken */
10571
Eric Andersencb57d552001-06-28 07:25:16 +000010572/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010573 * Read the next input token.
10574 * If the token is a word, we set backquotelist to the list of cmds in
10575 * backquotes. We set quoteflag to true if any part of the word was
10576 * quoted.
10577 * If the token is TREDIR, then we set redirnode to a structure containing
10578 * the redirection.
10579 * In all cases, the variable startlinno is set to the number of the line
10580 * on which the token starts.
10581 *
10582 * [Change comment: here documents and internal procedures]
10583 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10584 * word parsing code into a separate routine. In this case, readtoken
10585 * doesn't need to have any internal procedures, but parseword does.
10586 * We could also make parseoperator in essence the main routine, and
10587 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010588 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010589#define NEW_xxreadtoken
10590#ifdef NEW_xxreadtoken
10591/* singles must be first! */
10592static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010593
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010594static const char xxreadtoken_tokens[] = {
10595 TNL, TLP, TRP, /* only single occurrence allowed */
10596 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10597 TEOF, /* corresponds to trailing nul */
10598 TAND, TOR, TENDCASE, /* if double occurrence */
10599};
10600
10601#define xxreadtoken_doubles \
10602 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10603#define xxreadtoken_singles \
10604 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10605
10606static int
10607xxreadtoken(void)
10608{
10609 int c;
10610
10611 if (tokpushback) {
10612 tokpushback = 0;
10613 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010614 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010615 if (needprompt) {
10616 setprompt(2);
10617 }
10618 startlinno = plinno;
10619 for (;;) { /* until token or start of word found */
10620 c = pgetc_macro();
10621
10622 if ((c != ' ') && (c != '\t')
10623#if ENABLE_ASH_ALIAS
10624 && (c != PEOA)
10625#endif
10626 ) {
10627 if (c == '#') {
10628 while ((c = pgetc()) != '\n' && c != PEOF);
10629 pungetc();
10630 } else if (c == '\\') {
10631 if (pgetc() != '\n') {
10632 pungetc();
10633 goto READTOKEN1;
10634 }
10635 startlinno = ++plinno;
10636 if (doprompt)
10637 setprompt(2);
10638 } else {
10639 const char *p
10640 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10641
10642 if (c != PEOF) {
10643 if (c == '\n') {
10644 plinno++;
10645 needprompt = doprompt;
10646 }
10647
10648 p = strchr(xxreadtoken_chars, c);
10649 if (p == NULL) {
10650 READTOKEN1:
10651 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10652 }
10653
10654 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10655 if (pgetc() == *p) { /* double occurrence? */
10656 p += xxreadtoken_doubles + 1;
10657 } else {
10658 pungetc();
10659 }
10660 }
10661 }
10662 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10663 }
10664 }
10665 } /* for */
10666}
10667#else
10668#define RETURN(token) return lasttoken = token
10669static int
10670xxreadtoken(void)
10671{
10672 int c;
10673
10674 if (tokpushback) {
10675 tokpushback = 0;
10676 return lasttoken;
10677 }
10678 if (needprompt) {
10679 setprompt(2);
10680 }
10681 startlinno = plinno;
10682 for (;;) { /* until token or start of word found */
10683 c = pgetc_macro();
10684 switch (c) {
10685 case ' ': case '\t':
10686#if ENABLE_ASH_ALIAS
10687 case PEOA:
10688#endif
10689 continue;
10690 case '#':
10691 while ((c = pgetc()) != '\n' && c != PEOF);
10692 pungetc();
10693 continue;
10694 case '\\':
10695 if (pgetc() == '\n') {
10696 startlinno = ++plinno;
10697 if (doprompt)
10698 setprompt(2);
10699 continue;
10700 }
10701 pungetc();
10702 goto breakloop;
10703 case '\n':
10704 plinno++;
10705 needprompt = doprompt;
10706 RETURN(TNL);
10707 case PEOF:
10708 RETURN(TEOF);
10709 case '&':
10710 if (pgetc() == '&')
10711 RETURN(TAND);
10712 pungetc();
10713 RETURN(TBACKGND);
10714 case '|':
10715 if (pgetc() == '|')
10716 RETURN(TOR);
10717 pungetc();
10718 RETURN(TPIPE);
10719 case ';':
10720 if (pgetc() == ';')
10721 RETURN(TENDCASE);
10722 pungetc();
10723 RETURN(TSEMI);
10724 case '(':
10725 RETURN(TLP);
10726 case ')':
10727 RETURN(TRP);
10728 default:
10729 goto breakloop;
10730 }
10731 }
10732 breakloop:
10733 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10734#undef RETURN
10735}
10736#endif /* NEW_xxreadtoken */
10737
10738static int
10739readtoken(void)
10740{
10741 int t;
10742#if DEBUG
10743 int alreadyseen = tokpushback;
10744#endif
10745
10746#if ENABLE_ASH_ALIAS
10747 top:
10748#endif
10749
10750 t = xxreadtoken();
10751
10752 /*
10753 * eat newlines
10754 */
10755 if (checkkwd & CHKNL) {
10756 while (t == TNL) {
10757 parseheredoc();
10758 t = xxreadtoken();
10759 }
10760 }
10761
10762 if (t != TWORD || quoteflag) {
10763 goto out;
10764 }
10765
10766 /*
10767 * check for keywords
10768 */
10769 if (checkkwd & CHKKWD) {
10770 const char *const *pp;
10771
10772 pp = findkwd(wordtext);
10773 if (pp) {
10774 lasttoken = t = pp - tokname_array;
10775 TRACE(("keyword %s recognized\n", tokname(t)));
10776 goto out;
10777 }
10778 }
10779
10780 if (checkkwd & CHKALIAS) {
10781#if ENABLE_ASH_ALIAS
10782 struct alias *ap;
10783 ap = lookupalias(wordtext, 1);
10784 if (ap != NULL) {
10785 if (*ap->val) {
10786 pushstring(ap->val, ap);
10787 }
10788 goto top;
10789 }
10790#endif
10791 }
10792 out:
10793 checkkwd = 0;
10794#if DEBUG
10795 if (!alreadyseen)
10796 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10797 else
10798 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10799#endif
10800 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010801}
10802
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010803static char
10804peektoken(void)
10805{
10806 int t;
10807
10808 t = readtoken();
10809 tokpushback++;
10810 return tokname_array[t][0];
10811}
Eric Andersencb57d552001-06-28 07:25:16 +000010812
10813/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010814 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10815 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010816 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010817static union node *
10818parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010819{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010820 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010821
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010822 tokpushback = 0;
10823 doprompt = interact;
10824 if (doprompt)
10825 setprompt(doprompt);
10826 needprompt = 0;
10827 t = readtoken();
10828 if (t == TEOF)
10829 return NEOF;
10830 if (t == TNL)
10831 return NULL;
10832 tokpushback++;
10833 return list(1);
10834}
10835
10836/*
10837 * Input any here documents.
10838 */
10839static void
10840parseheredoc(void)
10841{
10842 struct heredoc *here;
10843 union node *n;
10844
10845 here = heredoclist;
10846 heredoclist = 0;
10847
10848 while (here) {
10849 if (needprompt) {
10850 setprompt(2);
10851 }
10852 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10853 here->eofmark, here->striptabs);
10854 n = stalloc(sizeof(struct narg));
10855 n->narg.type = NARG;
10856 n->narg.next = NULL;
10857 n->narg.text = wordtext;
10858 n->narg.backquote = backquotelist;
10859 here->here->nhere.doc = n;
10860 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010861 }
Eric Andersencb57d552001-06-28 07:25:16 +000010862}
10863
10864
10865/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010866 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010867 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010868#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010869static const char *
10870expandstr(const char *ps)
10871{
10872 union node n;
10873
10874 /* XXX Fix (char *) cast. */
10875 setinputstring((char *)ps);
10876 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10877 popfile();
10878
10879 n.narg.type = NARG;
10880 n.narg.next = NULL;
10881 n.narg.text = wordtext;
10882 n.narg.backquote = backquotelist;
10883
10884 expandarg(&n, NULL, 0);
10885 return stackblock();
10886}
10887#endif
10888
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010889/*
10890 * Execute a command or commands contained in a string.
10891 */
10892static int
10893evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010894{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010895 union node *n;
10896 struct stackmark smark;
10897 int skip;
10898
10899 setinputstring(s);
10900 setstackmark(&smark);
10901
10902 skip = 0;
10903 while ((n = parsecmd(0)) != NEOF) {
10904 evaltree(n, 0);
10905 popstackmark(&smark);
10906 skip = evalskip;
10907 if (skip)
10908 break;
10909 }
10910 popfile();
10911
10912 skip &= mask;
10913 evalskip = skip;
10914 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010915}
10916
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010917/*
10918 * The eval command.
10919 */
10920static int
10921evalcmd(int argc, char **argv)
10922{
10923 char *p;
10924 char *concat;
10925 char **ap;
10926
10927 if (argc > 1) {
10928 p = argv[1];
10929 if (argc > 2) {
10930 STARTSTACKSTR(concat);
10931 ap = argv + 2;
10932 for (;;) {
10933 concat = stack_putstr(p, concat);
10934 p = *ap++;
10935 if (p == NULL)
10936 break;
10937 STPUTC(' ', concat);
10938 }
10939 STPUTC('\0', concat);
10940 p = grabstackstr(concat);
10941 }
10942 evalstring(p, ~SKIPEVAL);
10943
10944 }
10945 return exitstatus;
10946}
10947
10948/*
10949 * Read and execute commands. "Top" is nonzero for the top level command
10950 * loop; it turns on prompting if the shell is interactive.
10951 */
10952static int
10953cmdloop(int top)
10954{
10955 union node *n;
10956 struct stackmark smark;
10957 int inter;
10958 int numeof = 0;
10959
10960 TRACE(("cmdloop(%d) called\n", top));
10961 for (;;) {
10962 int skip;
10963
10964 setstackmark(&smark);
10965#if JOBS
10966 if (jobctl)
10967 showjobs(stderr, SHOW_CHANGED);
10968#endif
10969 inter = 0;
10970 if (iflag && top) {
10971 inter++;
10972#if ENABLE_ASH_MAIL
10973 chkmail();
10974#endif
10975 }
10976 n = parsecmd(inter);
10977 /* showtree(n); DEBUG */
10978 if (n == NEOF) {
10979 if (!top || numeof >= 50)
10980 break;
10981 if (!stoppedjobs()) {
10982 if (!Iflag)
10983 break;
10984 out2str("\nUse \"exit\" to leave shell.\n");
10985 }
10986 numeof++;
10987 } else if (nflag == 0) {
10988 job_warning = (job_warning == 2) ? 1 : 0;
10989 numeof = 0;
10990 evaltree(n, 0);
10991 }
10992 popstackmark(&smark);
10993 skip = evalskip;
10994
10995 if (skip) {
10996 evalskip = 0;
10997 return skip & SKIPEVAL;
10998 }
10999 }
11000 return 0;
11001}
11002
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011003/*
11004 * Take commands from a file. To be compatible we should do a path
11005 * search for the file, which is necessary to find sub-commands.
11006 */
11007static char *
11008find_dot_file(char *name)
11009{
11010 char *fullname;
11011 const char *path = pathval();
11012 struct stat statb;
11013
11014 /* don't try this for absolute or relative paths */
11015 if (strchr(name, '/'))
11016 return name;
11017
11018 while ((fullname = padvance(&path, name)) != NULL) {
11019 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11020 /*
11021 * Don't bother freeing here, since it will
11022 * be freed by the caller.
11023 */
11024 return fullname;
11025 }
11026 stunalloc(fullname);
11027 }
11028
11029 /* not found in the PATH */
11030 ash_msg_and_raise_error("%s: not found", name);
11031 /* NOTREACHED */
11032}
11033
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011034static int
11035dotcmd(int argc, char **argv)
11036{
11037 struct strlist *sp;
11038 volatile struct shparam saveparam;
11039 int status = 0;
11040
11041 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011042 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011043
11044 if (argc >= 2) { /* That's what SVR2 does */
11045 char *fullname;
11046
11047 fullname = find_dot_file(argv[1]);
11048
11049 if (argc > 2) {
11050 saveparam = shellparam;
11051 shellparam.malloc = 0;
11052 shellparam.nparam = argc - 2;
11053 shellparam.p = argv + 2;
11054 };
11055
11056 setinputfile(fullname, INPUT_PUSH_FILE);
11057 commandname = fullname;
11058 cmdloop(0);
11059 popfile();
11060
11061 if (argc > 2) {
11062 freeparam(&shellparam);
11063 shellparam = saveparam;
11064 };
11065 status = exitstatus;
11066 }
11067 return status;
11068}
11069
11070static int
11071exitcmd(int argc, char **argv)
11072{
11073 if (stoppedjobs())
11074 return 0;
11075 if (argc > 1)
11076 exitstatus = number(argv[1]);
11077 raise_exception(EXEXIT);
11078 /* NOTREACHED */
11079}
11080
11081#if ENABLE_ASH_BUILTIN_ECHO
11082static int
11083echocmd(int argc, char **argv)
11084{
11085 return bb_echo(argv);
11086}
11087#endif
11088
11089#if ENABLE_ASH_BUILTIN_TEST
11090static int
11091testcmd(int argc, char **argv)
11092{
11093 return bb_test(argc, argv);
11094}
11095#endif
11096
11097/*
11098 * Read a file containing shell functions.
11099 */
11100static void
11101readcmdfile(char *name)
11102{
11103 setinputfile(name, INPUT_PUSH_FILE);
11104 cmdloop(0);
11105 popfile();
11106}
11107
11108
Denis Vlasenkocc571512007-02-23 21:10:35 +000011109/* ============ find_command inplementation */
11110
11111/*
11112 * Resolve a command name. If you change this routine, you may have to
11113 * change the shellexec routine as well.
11114 */
11115static void
11116find_command(char *name, struct cmdentry *entry, int act, const char *path)
11117{
11118 struct tblentry *cmdp;
11119 int idx;
11120 int prev;
11121 char *fullname;
11122 struct stat statb;
11123 int e;
11124 int updatetbl;
11125 struct builtincmd *bcmd;
11126
11127 /* If name contains a slash, don't use PATH or hash table */
11128 if (strchr(name, '/') != NULL) {
11129 entry->u.index = -1;
11130 if (act & DO_ABS) {
11131 while (stat(name, &statb) < 0) {
11132#ifdef SYSV
11133 if (errno == EINTR)
11134 continue;
11135#endif
11136 entry->cmdtype = CMDUNKNOWN;
11137 return;
11138 }
11139 }
11140 entry->cmdtype = CMDNORMAL;
11141 return;
11142 }
11143
11144#if ENABLE_FEATURE_SH_STANDALONE_SHELL
11145 if (find_applet_by_name(name)) {
11146 entry->cmdtype = CMDNORMAL;
11147 entry->u.index = -1;
11148 return;
11149 }
Denis Vlasenko29e31dd2007-03-03 23:12:17 +000011150 /* Already caught above
Denis Vlasenkocc571512007-02-23 21:10:35 +000011151 if (is_safe_applet(name)) {
11152 entry->cmdtype = CMDNORMAL;
11153 entry->u.index = -1;
11154 return;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +000011155 }*/
11156#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000011157
11158 updatetbl = (path == pathval());
11159 if (!updatetbl) {
11160 act |= DO_ALTPATH;
11161 if (strstr(path, "%builtin") != NULL)
11162 act |= DO_ALTBLTIN;
11163 }
11164
11165 /* If name is in the table, check answer will be ok */
11166 cmdp = cmdlookup(name, 0);
11167 if (cmdp != NULL) {
11168 int bit;
11169
11170 switch (cmdp->cmdtype) {
11171 default:
11172#if DEBUG
11173 abort();
11174#endif
11175 case CMDNORMAL:
11176 bit = DO_ALTPATH;
11177 break;
11178 case CMDFUNCTION:
11179 bit = DO_NOFUNC;
11180 break;
11181 case CMDBUILTIN:
11182 bit = DO_ALTBLTIN;
11183 break;
11184 }
11185 if (act & bit) {
11186 updatetbl = 0;
11187 cmdp = NULL;
11188 } else if (cmdp->rehash == 0)
11189 /* if not invalidated by cd, we're done */
11190 goto success;
11191 }
11192
11193 /* If %builtin not in path, check for builtin next */
11194 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011195 if (bcmd) {
11196 if (IS_BUILTIN_REGULAR(bcmd))
11197 goto builtin_success;
11198 if (act & DO_ALTPATH) {
11199 if (!(act & DO_ALTBLTIN))
11200 goto builtin_success;
11201 } else if (builtinloc <= 0) {
11202 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011203 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011204 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011205
11206 /* We have to search path. */
11207 prev = -1; /* where to start */
11208 if (cmdp && cmdp->rehash) { /* doing a rehash */
11209 if (cmdp->cmdtype == CMDBUILTIN)
11210 prev = builtinloc;
11211 else
11212 prev = cmdp->param.index;
11213 }
11214
11215 e = ENOENT;
11216 idx = -1;
11217 loop:
11218 while ((fullname = padvance(&path, name)) != NULL) {
11219 stunalloc(fullname);
11220 idx++;
11221 if (pathopt) {
11222 if (prefix(pathopt, "builtin")) {
11223 if (bcmd)
11224 goto builtin_success;
11225 continue;
11226 } else if (!(act & DO_NOFUNC) &&
11227 prefix(pathopt, "func")) {
11228 /* handled below */
11229 } else {
11230 /* ignore unimplemented options */
11231 continue;
11232 }
11233 }
11234 /* if rehash, don't redo absolute path names */
11235 if (fullname[0] == '/' && idx <= prev) {
11236 if (idx < prev)
11237 continue;
11238 TRACE(("searchexec \"%s\": no change\n", name));
11239 goto success;
11240 }
11241 while (stat(fullname, &statb) < 0) {
11242#ifdef SYSV
11243 if (errno == EINTR)
11244 continue;
11245#endif
11246 if (errno != ENOENT && errno != ENOTDIR)
11247 e = errno;
11248 goto loop;
11249 }
11250 e = EACCES; /* if we fail, this will be the error */
11251 if (!S_ISREG(statb.st_mode))
11252 continue;
11253 if (pathopt) { /* this is a %func directory */
11254 stalloc(strlen(fullname) + 1);
11255 readcmdfile(fullname);
11256 cmdp = cmdlookup(name, 0);
11257 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11258 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11259 stunalloc(fullname);
11260 goto success;
11261 }
11262 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11263 if (!updatetbl) {
11264 entry->cmdtype = CMDNORMAL;
11265 entry->u.index = idx;
11266 return;
11267 }
11268 INT_OFF;
11269 cmdp = cmdlookup(name, 1);
11270 cmdp->cmdtype = CMDNORMAL;
11271 cmdp->param.index = idx;
11272 INT_ON;
11273 goto success;
11274 }
11275
11276 /* We failed. If there was an entry for this command, delete it */
11277 if (cmdp && updatetbl)
11278 delete_cmd_entry();
11279 if (act & DO_ERR)
11280 ash_msg("%s: %s", name, errmsg(e, "not found"));
11281 entry->cmdtype = CMDUNKNOWN;
11282 return;
11283
11284 builtin_success:
11285 if (!updatetbl) {
11286 entry->cmdtype = CMDBUILTIN;
11287 entry->u.cmd = bcmd;
11288 return;
11289 }
11290 INT_OFF;
11291 cmdp = cmdlookup(name, 1);
11292 cmdp->cmdtype = CMDBUILTIN;
11293 cmdp->param.cmd = bcmd;
11294 INT_ON;
11295 success:
11296 cmdp->rehash = 0;
11297 entry->cmdtype = cmdp->cmdtype;
11298 entry->u = cmdp->param;
11299}
11300
11301
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011302/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011303
Eric Andersencb57d552001-06-28 07:25:16 +000011304/*
Eric Andersencb57d552001-06-28 07:25:16 +000011305 * The trap builtin.
11306 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011307static int
Eric Andersenc470f442003-07-28 09:56:35 +000011308trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011309{
11310 char *action;
11311 char **ap;
11312 int signo;
11313
Eric Andersenc470f442003-07-28 09:56:35 +000011314 nextopt(nullstr);
11315 ap = argptr;
11316 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011317 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011318 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011319 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011320
Rob Landleyc9c1a412006-07-12 19:17:55 +000011321 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011322 out1fmt("trap -- %s %s\n",
11323 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011324 }
11325 }
11326 return 0;
11327 }
Eric Andersenc470f442003-07-28 09:56:35 +000011328 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011329 action = NULL;
11330 else
11331 action = *ap++;
11332 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011333 signo = get_signum(*ap);
11334 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011335 ash_msg_and_raise_error("%s: bad trap", *ap);
11336 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011337 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011338 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011339 action = NULL;
11340 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011341 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011342 }
Eric Andersenc470f442003-07-28 09:56:35 +000011343 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011344 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011345 trap[signo] = action;
11346 if (signo != 0)
11347 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011348 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011349 ap++;
11350 }
11351 return 0;
11352}
11353
Eric Andersenc470f442003-07-28 09:56:35 +000011354
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011355/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011356
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011357#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011358/*
11359 * Lists available builtins
11360 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011361static int
11362helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011363{
11364 int col, i;
11365
11366 out1fmt("\nBuilt-in commands:\n-------------------\n");
11367 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11368 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011369 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011370 if (col > 60) {
11371 out1fmt("\n");
11372 col = 0;
11373 }
11374 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011375#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011376 for (i = 0; i < NUM_APPLETS; i++) {
11377 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11378 if (col > 60) {
11379 out1fmt("\n");
11380 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011381 }
11382 }
11383#endif
11384 out1fmt("\n\n");
11385 return EXIT_SUCCESS;
11386}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011387#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011388
Eric Andersencb57d552001-06-28 07:25:16 +000011389/*
Eric Andersencb57d552001-06-28 07:25:16 +000011390 * The export and readonly commands.
11391 */
Eric Andersenc470f442003-07-28 09:56:35 +000011392static int
11393exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011394{
11395 struct var *vp;
11396 char *name;
11397 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011398 char **aptr;
11399 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011400
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011401 if (nextopt("p") != 'p') {
11402 aptr = argptr;
11403 name = *aptr;
11404 if (name) {
11405 do {
11406 p = strchr(name, '=');
11407 if (p != NULL) {
11408 p++;
11409 } else {
11410 vp = *findvar(hashvar(name), name);
11411 if (vp) {
11412 vp->flags |= flag;
11413 continue;
11414 }
Eric Andersencb57d552001-06-28 07:25:16 +000011415 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011416 setvar(name, p, flag);
11417 } while ((name = *++aptr) != NULL);
11418 return 0;
11419 }
Eric Andersencb57d552001-06-28 07:25:16 +000011420 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011421 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011422 return 0;
11423}
11424
Eric Andersencb57d552001-06-28 07:25:16 +000011425/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011426 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011427 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011428static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011429unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011430{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011431 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011432
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011433 cmdp = cmdlookup(name, 0);
11434 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11435 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011436}
11437
Eric Andersencb57d552001-06-28 07:25:16 +000011438/*
Eric Andersencb57d552001-06-28 07:25:16 +000011439 * The unset builtin command. We unset the function before we unset the
11440 * variable to allow a function to be unset when there is a readonly variable
11441 * with the same name.
11442 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011443static int
Eric Andersenc470f442003-07-28 09:56:35 +000011444unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011445{
11446 char **ap;
11447 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011448 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011449 int ret = 0;
11450
11451 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011452 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011453 }
Eric Andersencb57d552001-06-28 07:25:16 +000011454
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011455 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011456 if (flag != 'f') {
11457 i = unsetvar(*ap);
11458 ret |= i;
11459 if (!(i & 2))
11460 continue;
11461 }
11462 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011463 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011464 }
Eric Andersenc470f442003-07-28 09:56:35 +000011465 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011466}
11467
11468
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011469/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011470
Eric Andersenc470f442003-07-28 09:56:35 +000011471#include <sys/times.h>
11472
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011473static const unsigned char timescmd_str[] = {
11474 ' ', offsetof(struct tms, tms_utime),
11475 '\n', offsetof(struct tms, tms_stime),
11476 ' ', offsetof(struct tms, tms_cutime),
11477 '\n', offsetof(struct tms, tms_cstime),
11478 0
11479};
Eric Andersencb57d552001-06-28 07:25:16 +000011480
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011481static int
11482timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011483{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011484 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011485 const unsigned char *p;
11486 struct tms buf;
11487
11488 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011489 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011490
11491 p = timescmd_str;
11492 do {
11493 t = *(clock_t *)(((char *) &buf) + p[1]);
11494 s = t / clk_tck;
11495 out1fmt("%ldm%ld.%.3lds%c",
11496 s/60, s%60,
11497 ((t - s * clk_tck) * 1000) / clk_tck,
11498 p[0]);
11499 } while (*(p += 2));
11500
Eric Andersencb57d552001-06-28 07:25:16 +000011501 return 0;
11502}
11503
Denis Vlasenko131ae172007-02-18 13:00:19 +000011504#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011505static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011506dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011507{
Eric Andersened9ecf72004-06-22 08:29:45 +000011508 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011509 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011510
Denis Vlasenkob012b102007-02-19 22:43:01 +000011511 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011512 result = arith(s, &errcode);
11513 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011514 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011515 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011516 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011517 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011518 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011519 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011520 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011521 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011522 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011523
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011524 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011525}
Eric Andersenc470f442003-07-28 09:56:35 +000011526
Eric Andersenc470f442003-07-28 09:56:35 +000011527/*
Eric Andersen90898442003-08-06 11:20:52 +000011528 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11529 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11530 *
11531 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011532 */
11533static int
Eric Andersen90898442003-08-06 11:20:52 +000011534letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011535{
Eric Andersenc470f442003-07-28 09:56:35 +000011536 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011537 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011538
Eric Andersen90898442003-08-06 11:20:52 +000011539 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011540 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011541 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011542 for (ap = argv + 1; *ap; ap++) {
11543 i = dash_arith(*ap);
11544 }
Eric Andersenc470f442003-07-28 09:56:35 +000011545
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011546 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011547}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011548#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011549
Eric Andersenc470f442003-07-28 09:56:35 +000011550
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011551/* ============ miscbltin.c
11552 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011553 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011554 */
11555
11556#undef rflag
11557
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011558#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011559typedef enum __rlimit_resource rlim_t;
11560#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011561
Eric Andersenc470f442003-07-28 09:56:35 +000011562/*
11563 * The read builtin. The -e option causes backslashes to escape the
11564 * following character.
11565 *
11566 * This uses unbuffered input, which may be avoidable in some cases.
11567 */
Eric Andersenc470f442003-07-28 09:56:35 +000011568static int
11569readcmd(int argc, char **argv)
11570{
11571 char **ap;
11572 int backslash;
11573 char c;
11574 int rflag;
11575 char *prompt;
11576 const char *ifs;
11577 char *p;
11578 int startword;
11579 int status;
11580 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011581#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011582 int nch_flag = 0;
11583 int nchars = 0;
11584 int silent = 0;
11585 struct termios tty, old_tty;
11586#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011587#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011588 fd_set set;
11589 struct timeval ts;
11590
11591 ts.tv_sec = ts.tv_usec = 0;
11592#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011593
11594 rflag = 0;
11595 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011596#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011597 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011598#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011599 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011600#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011601 while ((i = nextopt("p:rt:")) != '\0')
11602#else
11603 while ((i = nextopt("p:r")) != '\0')
11604#endif
11605 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011606 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011607 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011608 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011609 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011610#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011611 case 'n':
11612 nchars = strtol(optionarg, &p, 10);
11613 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011614 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011615 nch_flag = (nchars > 0);
11616 break;
11617 case 's':
11618 silent = 1;
11619 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011620#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011621#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011622 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011623 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011624 ts.tv_usec = 0;
11625 if (*p == '.') {
11626 char *p2;
11627 if (*++p) {
11628 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011629 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011630 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011631 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011632 scale = p2 - p;
11633 /* normalize to usec */
11634 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011635 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011636 while (scale++ < 6)
11637 ts.tv_usec *= 10;
11638 }
11639 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011640 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011641 }
11642 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011643 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011644 break;
11645#endif
11646 case 'r':
11647 rflag = 1;
11648 break;
11649 default:
11650 break;
11651 }
Eric Andersenc470f442003-07-28 09:56:35 +000011652 }
11653 if (prompt && isatty(0)) {
11654 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011655 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011656 ap = argptr;
11657 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011658 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011659 ifs = bltinlookup("IFS");
11660 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011661 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011662#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011663 if (nch_flag || silent) {
11664 tcgetattr(0, &tty);
11665 old_tty = tty;
11666 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011667 tty.c_lflag &= ~ICANON;
11668 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011669 }
11670 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011671 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011672
11673 }
11674 tcsetattr(0, TCSANOW, &tty);
11675 }
11676#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011677#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011678 if (ts.tv_sec || ts.tv_usec) {
11679 FD_ZERO (&set);
11680 FD_SET (0, &set);
11681
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011682 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011683 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011684#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011685 if (nch_flag)
11686 tcsetattr(0, TCSANOW, &old_tty);
11687#endif
11688 return 1;
11689 }
11690 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011691#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011692 status = 0;
11693 startword = 1;
11694 backslash = 0;
11695 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011696#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011697 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011698#else
11699 for (;;)
11700#endif
11701 {
Eric Andersenc470f442003-07-28 09:56:35 +000011702 if (read(0, &c, 1) != 1) {
11703 status = 1;
11704 break;
11705 }
11706 if (c == '\0')
11707 continue;
11708 if (backslash) {
11709 backslash = 0;
11710 if (c != '\n')
11711 goto put;
11712 continue;
11713 }
11714 if (!rflag && c == '\\') {
11715 backslash++;
11716 continue;
11717 }
11718 if (c == '\n')
11719 break;
11720 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11721 continue;
11722 }
11723 startword = 0;
11724 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11725 STACKSTRNUL(p);
11726 setvar(*ap, stackblock(), 0);
11727 ap++;
11728 startword = 1;
11729 STARTSTACKSTR(p);
11730 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011731 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011732 STPUTC(c, p);
11733 }
11734 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011735#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011736 if (nch_flag || silent)
11737 tcsetattr(0, TCSANOW, &old_tty);
11738#endif
11739
Eric Andersenc470f442003-07-28 09:56:35 +000011740 STACKSTRNUL(p);
11741 /* Remove trailing blanks */
11742 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11743 *p = '\0';
11744 setvar(*ap, stackblock(), 0);
11745 while (*++ap != NULL)
11746 setvar(*ap, nullstr, 0);
11747 return status;
11748}
11749
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011750static int
11751umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011752{
11753 static const char permuser[3] = "ugo";
11754 static const char permmode[3] = "rwx";
11755 static const short int permmask[] = {
11756 S_IRUSR, S_IWUSR, S_IXUSR,
11757 S_IRGRP, S_IWGRP, S_IXGRP,
11758 S_IROTH, S_IWOTH, S_IXOTH
11759 };
11760
11761 char *ap;
11762 mode_t mask;
11763 int i;
11764 int symbolic_mode = 0;
11765
11766 while (nextopt("S") != '\0') {
11767 symbolic_mode = 1;
11768 }
11769
Denis Vlasenkob012b102007-02-19 22:43:01 +000011770 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011771 mask = umask(0);
11772 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011773 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011774
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011775 ap = *argptr;
11776 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011777 if (symbolic_mode) {
11778 char buf[18];
11779 char *p = buf;
11780
11781 for (i = 0; i < 3; i++) {
11782 int j;
11783
11784 *p++ = permuser[i];
11785 *p++ = '=';
11786 for (j = 0; j < 3; j++) {
11787 if ((mask & permmask[3 * i + j]) == 0) {
11788 *p++ = permmode[j];
11789 }
11790 }
11791 *p++ = ',';
11792 }
11793 *--p = 0;
11794 puts(buf);
11795 } else {
11796 out1fmt("%.4o\n", mask);
11797 }
11798 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011799 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011800 mask = 0;
11801 do {
11802 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011803 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011804 mask = (mask << 3) + (*ap - '0');
11805 } while (*++ap != '\0');
11806 umask(mask);
11807 } else {
11808 mask = ~mask & 0777;
11809 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011810 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011811 }
11812 umask(~mask & 0777);
11813 }
11814 }
11815 return 0;
11816}
11817
11818/*
11819 * ulimit builtin
11820 *
11821 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11822 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11823 * ash by J.T. Conklin.
11824 *
11825 * Public domain.
11826 */
11827
11828struct limits {
11829 const char *name;
11830 int cmd;
11831 int factor; /* multiply by to get rlim_{cur,max} values */
11832 char option;
11833};
11834
11835static const struct limits limits[] = {
11836#ifdef RLIMIT_CPU
11837 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11838#endif
11839#ifdef RLIMIT_FSIZE
11840 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11841#endif
11842#ifdef RLIMIT_DATA
11843 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11844#endif
11845#ifdef RLIMIT_STACK
11846 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11847#endif
11848#ifdef RLIMIT_CORE
11849 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11850#endif
11851#ifdef RLIMIT_RSS
11852 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11853#endif
11854#ifdef RLIMIT_MEMLOCK
11855 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11856#endif
11857#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011858 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011859#endif
11860#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011861 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011862#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011863#ifdef RLIMIT_AS
11864 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011865#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011866#ifdef RLIMIT_LOCKS
11867 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011868#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011869 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011870};
11871
Glenn L McGrath76620622004-01-13 10:19:37 +000011872enum limtype { SOFT = 0x1, HARD = 0x2 };
11873
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011874static void
11875printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011876 const struct limits *l)
11877{
11878 rlim_t val;
11879
11880 val = limit->rlim_max;
11881 if (how & SOFT)
11882 val = limit->rlim_cur;
11883
11884 if (val == RLIM_INFINITY)
11885 out1fmt("unlimited\n");
11886 else {
11887 val /= l->factor;
11888 out1fmt("%lld\n", (long long) val);
11889 }
11890}
11891
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011892static int
Eric Andersenc470f442003-07-28 09:56:35 +000011893ulimitcmd(int argc, char **argv)
11894{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011895 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011896 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011897 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011898 const struct limits *l;
11899 int set, all = 0;
11900 int optc, what;
11901 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011902
11903 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011904 while ((optc = nextopt("HSa"
11905#ifdef RLIMIT_CPU
11906 "t"
11907#endif
11908#ifdef RLIMIT_FSIZE
11909 "f"
11910#endif
11911#ifdef RLIMIT_DATA
11912 "d"
11913#endif
11914#ifdef RLIMIT_STACK
11915 "s"
11916#endif
11917#ifdef RLIMIT_CORE
11918 "c"
11919#endif
11920#ifdef RLIMIT_RSS
11921 "m"
11922#endif
11923#ifdef RLIMIT_MEMLOCK
11924 "l"
11925#endif
11926#ifdef RLIMIT_NPROC
11927 "p"
11928#endif
11929#ifdef RLIMIT_NOFILE
11930 "n"
11931#endif
11932#ifdef RLIMIT_AS
11933 "v"
11934#endif
11935#ifdef RLIMIT_LOCKS
11936 "w"
11937#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011938 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011939 switch (optc) {
11940 case 'H':
11941 how = HARD;
11942 break;
11943 case 'S':
11944 how = SOFT;
11945 break;
11946 case 'a':
11947 all = 1;
11948 break;
11949 default:
11950 what = optc;
11951 }
11952
Glenn L McGrath76620622004-01-13 10:19:37 +000011953 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011954 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011955
11956 set = *argptr ? 1 : 0;
11957 if (set) {
11958 char *p = *argptr;
11959
11960 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011961 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011962 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011963 val = RLIM_INFINITY;
11964 else {
11965 val = (rlim_t) 0;
11966
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011967 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011968 val = (val * 10) + (long)(c - '0');
11969 if (val < (rlim_t) 0)
11970 break;
11971 }
11972 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011973 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011974 val *= l->factor;
11975 }
11976 }
11977 if (all) {
11978 for (l = limits; l->name; l++) {
11979 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011980 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011981 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011982 }
11983 return 0;
11984 }
11985
11986 getrlimit(l->cmd, &limit);
11987 if (set) {
11988 if (how & HARD)
11989 limit.rlim_max = val;
11990 if (how & SOFT)
11991 limit.rlim_cur = val;
11992 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011993 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011994 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011995 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011996 }
11997 return 0;
11998}
11999
Eric Andersen90898442003-08-06 11:20:52 +000012000
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012001/* ============ Math support */
12002
Denis Vlasenko131ae172007-02-18 13:00:19 +000012003#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012004
12005/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12006
12007 Permission is hereby granted, free of charge, to any person obtaining
12008 a copy of this software and associated documentation files (the
12009 "Software"), to deal in the Software without restriction, including
12010 without limitation the rights to use, copy, modify, merge, publish,
12011 distribute, sublicense, and/or sell copies of the Software, and to
12012 permit persons to whom the Software is furnished to do so, subject to
12013 the following conditions:
12014
12015 The above copyright notice and this permission notice shall be
12016 included in all copies or substantial portions of the Software.
12017
12018 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12019 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12020 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12021 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12022 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12023 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12024 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12025*/
12026
12027/* This is my infix parser/evaluator. It is optimized for size, intended
12028 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012029 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012030 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012031 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012032 * be that which POSIX specifies for shells. */
12033
12034/* The code uses a simple two-stack algorithm. See
12035 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012036 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012037 * this is based (this code differs in that it applies operators immediately
12038 * to the stack instead of adding them to a queue to end up with an
12039 * expression). */
12040
12041/* To use the routine, call it with an expression string and error return
12042 * pointer */
12043
12044/*
12045 * Aug 24, 2001 Manuel Novoa III
12046 *
12047 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12048 *
12049 * 1) In arith_apply():
12050 * a) Cached values of *numptr and &(numptr[-1]).
12051 * b) Removed redundant test for zero denominator.
12052 *
12053 * 2) In arith():
12054 * a) Eliminated redundant code for processing operator tokens by moving
12055 * to a table-based implementation. Also folded handling of parens
12056 * into the table.
12057 * b) Combined all 3 loops which called arith_apply to reduce generated
12058 * code size at the cost of speed.
12059 *
12060 * 3) The following expressions were treated as valid by the original code:
12061 * 1() , 0! , 1 ( *3 ) .
12062 * These bugs have been fixed by internally enclosing the expression in
12063 * parens and then checking that all binary ops and right parens are
12064 * preceded by a valid expression (NUM_TOKEN).
12065 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012066 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012067 * ctype's isspace() if it is used by another busybox applet or if additional
12068 * whitespace chars should be considered. Look below the "#include"s for a
12069 * precompiler test.
12070 */
12071
12072/*
12073 * Aug 26, 2001 Manuel Novoa III
12074 *
12075 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12076 *
12077 * Merge in Aaron's comments previously posted to the busybox list,
12078 * modified slightly to take account of my changes to the code.
12079 *
12080 */
12081
12082/*
12083 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12084 *
12085 * - allow access to variable,
12086 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12087 * - realize assign syntax (VAR=expr, +=, *= etc)
12088 * - realize exponentiation (** operator)
12089 * - realize comma separated - expr, expr
12090 * - realise ++expr --expr expr++ expr--
12091 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012092 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012093 * - was restored loses XOR operator
12094 * - remove one goto label, added three ;-)
12095 * - protect $((num num)) as true zero expr (Manuel`s error)
12096 * - always use special isspace(), see comment from bash ;-)
12097 */
12098
Eric Andersen90898442003-08-06 11:20:52 +000012099#define arith_isspace(arithval) \
12100 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12101
Eric Andersen90898442003-08-06 11:20:52 +000012102typedef unsigned char operator;
12103
12104/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012105 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012106 * precedence. The ID portion is so that multiple operators can have the
12107 * same precedence, ensuring that the leftmost one is evaluated first.
12108 * Consider * and /. */
12109
12110#define tok_decl(prec,id) (((id)<<5)|(prec))
12111#define PREC(op) ((op) & 0x1F)
12112
12113#define TOK_LPAREN tok_decl(0,0)
12114
12115#define TOK_COMMA tok_decl(1,0)
12116
12117#define TOK_ASSIGN tok_decl(2,0)
12118#define TOK_AND_ASSIGN tok_decl(2,1)
12119#define TOK_OR_ASSIGN tok_decl(2,2)
12120#define TOK_XOR_ASSIGN tok_decl(2,3)
12121#define TOK_PLUS_ASSIGN tok_decl(2,4)
12122#define TOK_MINUS_ASSIGN tok_decl(2,5)
12123#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12124#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12125
12126#define TOK_MUL_ASSIGN tok_decl(3,0)
12127#define TOK_DIV_ASSIGN tok_decl(3,1)
12128#define TOK_REM_ASSIGN tok_decl(3,2)
12129
12130/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012131#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012132
12133/* conditional is right associativity too */
12134#define TOK_CONDITIONAL tok_decl(4,0)
12135#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12136
12137#define TOK_OR tok_decl(5,0)
12138
12139#define TOK_AND tok_decl(6,0)
12140
12141#define TOK_BOR tok_decl(7,0)
12142
12143#define TOK_BXOR tok_decl(8,0)
12144
12145#define TOK_BAND tok_decl(9,0)
12146
12147#define TOK_EQ tok_decl(10,0)
12148#define TOK_NE tok_decl(10,1)
12149
12150#define TOK_LT tok_decl(11,0)
12151#define TOK_GT tok_decl(11,1)
12152#define TOK_GE tok_decl(11,2)
12153#define TOK_LE tok_decl(11,3)
12154
12155#define TOK_LSHIFT tok_decl(12,0)
12156#define TOK_RSHIFT tok_decl(12,1)
12157
12158#define TOK_ADD tok_decl(13,0)
12159#define TOK_SUB tok_decl(13,1)
12160
12161#define TOK_MUL tok_decl(14,0)
12162#define TOK_DIV tok_decl(14,1)
12163#define TOK_REM tok_decl(14,2)
12164
12165/* exponent is right associativity */
12166#define TOK_EXPONENT tok_decl(15,1)
12167
12168/* For now unary operators. */
12169#define UNARYPREC 16
12170#define TOK_BNOT tok_decl(UNARYPREC,0)
12171#define TOK_NOT tok_decl(UNARYPREC,1)
12172
12173#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12174#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12175
12176#define PREC_PRE (UNARYPREC+2)
12177
12178#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12179#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12180
12181#define PREC_POST (UNARYPREC+3)
12182
12183#define TOK_POST_INC tok_decl(PREC_POST, 0)
12184#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12185
12186#define SPEC_PREC (UNARYPREC+4)
12187
12188#define TOK_NUM tok_decl(SPEC_PREC, 0)
12189#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12190
12191#define NUMPTR (*numstackptr)
12192
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012193static int
12194tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012195{
12196 operator prec = PREC(op);
12197
12198 convert_prec_is_assing(prec);
12199 return (prec == PREC(TOK_ASSIGN) ||
12200 prec == PREC_PRE || prec == PREC_POST);
12201}
12202
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012203static int
12204is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012205{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012206 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12207 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012208}
12209
Eric Andersen90898442003-08-06 11:20:52 +000012210typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012211 arith_t val;
12212 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012213 char contidional_second_val_initialized;
12214 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012215 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012216} v_n_t;
12217
Eric Andersen90898442003-08-06 11:20:52 +000012218typedef struct CHK_VAR_RECURSIVE_LOOPED {
12219 const char *var;
12220 struct CHK_VAR_RECURSIVE_LOOPED *next;
12221} chk_var_recursive_looped_t;
12222
12223static chk_var_recursive_looped_t *prev_chk_var_recursive;
12224
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012225static int
12226arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012227{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012228 if (t->var) {
12229 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012230
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012231 if (p) {
12232 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012233
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012234 /* recursive try as expression */
12235 chk_var_recursive_looped_t *cur;
12236 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012237
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012238 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12239 if (strcmp(cur->var, t->var) == 0) {
12240 /* expression recursion loop detected */
12241 return -5;
12242 }
12243 }
12244 /* save current lookuped var name */
12245 cur = prev_chk_var_recursive;
12246 cur_save.var = t->var;
12247 cur_save.next = cur;
12248 prev_chk_var_recursive = &cur_save;
12249
12250 t->val = arith (p, &errcode);
12251 /* restore previous ptr after recursiving */
12252 prev_chk_var_recursive = cur;
12253 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012254 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012255 /* allow undefined var as 0 */
12256 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012257 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012258 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012259}
12260
12261/* "applying" a token means performing it on the top elements on the integer
12262 * stack. For a unary operator it will only change the top element, but a
12263 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012264static int
12265arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012266{
Eric Andersen90898442003-08-06 11:20:52 +000012267 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012268 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012269 int ret_arith_lookup_val;
12270
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012271 /* There is no operator that can work without arguments */
12272 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012273 numptr_m1 = NUMPTR - 1;
12274
12275 /* check operand is var with noninteger value */
12276 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012277 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012278 return ret_arith_lookup_val;
12279
12280 rez = numptr_m1->val;
12281 if (op == TOK_UMINUS)
12282 rez *= -1;
12283 else if (op == TOK_NOT)
12284 rez = !rez;
12285 else if (op == TOK_BNOT)
12286 rez = ~rez;
12287 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12288 rez++;
12289 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12290 rez--;
12291 else if (op != TOK_UPLUS) {
12292 /* Binary operators */
12293
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012294 /* check and binary operators need two arguments */
12295 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012296
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012297 /* ... and they pop one */
12298 --NUMPTR;
12299 numptr_val = rez;
12300 if (op == TOK_CONDITIONAL) {
12301 if (! numptr_m1->contidional_second_val_initialized) {
12302 /* protect $((expr1 ? expr2)) without ": expr" */
12303 goto err;
12304 }
12305 rez = numptr_m1->contidional_second_val;
12306 } else if (numptr_m1->contidional_second_val_initialized) {
12307 /* protect $((expr1 : expr2)) without "expr ? " */
12308 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012309 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012310 numptr_m1 = NUMPTR - 1;
12311 if (op != TOK_ASSIGN) {
12312 /* check operand is var with noninteger value for not '=' */
12313 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12314 if (ret_arith_lookup_val)
12315 return ret_arith_lookup_val;
12316 }
12317 if (op == TOK_CONDITIONAL) {
12318 numptr_m1->contidional_second_val = rez;
12319 }
12320 rez = numptr_m1->val;
12321 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012322 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012323 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012324 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012325 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012326 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012327 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012328 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012329 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012330 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012331 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012332 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012333 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012334 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012335 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012336 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012337 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012338 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012339 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012340 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012341 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012342 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012343 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012344 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012345 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012346 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012347 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012348 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012349 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012350 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012351 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012352 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012353 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012354 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012355 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012356 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012357 /* protect $((expr : expr)) without "expr ? " */
12358 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012359 }
12360 numptr_m1->contidional_second_val_initialized = op;
12361 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012362 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012363 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012364 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012365 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012366 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012367 return -3; /* exponent less than 0 */
12368 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012369 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012370
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012371 if (numptr_val)
12372 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012373 c *= rez;
12374 rez = c;
12375 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012376 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012377 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012378 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012379 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012380 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012381 rez %= numptr_val;
12382 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012383 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012384 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012385
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012387 /* Hmm, 1=2 ? */
12388 goto err;
12389 }
12390 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012391#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012392 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012393#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012394 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012395#endif
Eric Andersen90898442003-08-06 11:20:52 +000012396 setvar(numptr_m1->var, buf, 0);
12397 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012398 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012399 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012400 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012401 rez++;
12402 }
12403 numptr_m1->val = rez;
12404 /* protect geting var value, is number now */
12405 numptr_m1->var = NULL;
12406 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012407 err:
12408 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012409}
12410
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012411/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012412static const char op_tokens[] = {
12413 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12414 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12415 '<','<', 0, TOK_LSHIFT,
12416 '>','>', 0, TOK_RSHIFT,
12417 '|','|', 0, TOK_OR,
12418 '&','&', 0, TOK_AND,
12419 '!','=', 0, TOK_NE,
12420 '<','=', 0, TOK_LE,
12421 '>','=', 0, TOK_GE,
12422 '=','=', 0, TOK_EQ,
12423 '|','=', 0, TOK_OR_ASSIGN,
12424 '&','=', 0, TOK_AND_ASSIGN,
12425 '*','=', 0, TOK_MUL_ASSIGN,
12426 '/','=', 0, TOK_DIV_ASSIGN,
12427 '%','=', 0, TOK_REM_ASSIGN,
12428 '+','=', 0, TOK_PLUS_ASSIGN,
12429 '-','=', 0, TOK_MINUS_ASSIGN,
12430 '-','-', 0, TOK_POST_DEC,
12431 '^','=', 0, TOK_XOR_ASSIGN,
12432 '+','+', 0, TOK_POST_INC,
12433 '*','*', 0, TOK_EXPONENT,
12434 '!', 0, TOK_NOT,
12435 '<', 0, TOK_LT,
12436 '>', 0, TOK_GT,
12437 '=', 0, TOK_ASSIGN,
12438 '|', 0, TOK_BOR,
12439 '&', 0, TOK_BAND,
12440 '*', 0, TOK_MUL,
12441 '/', 0, TOK_DIV,
12442 '%', 0, TOK_REM,
12443 '+', 0, TOK_ADD,
12444 '-', 0, TOK_SUB,
12445 '^', 0, TOK_BXOR,
12446 /* uniq */
12447 '~', 0, TOK_BNOT,
12448 ',', 0, TOK_COMMA,
12449 '?', 0, TOK_CONDITIONAL,
12450 ':', 0, TOK_CONDITIONAL_SEP,
12451 ')', 0, TOK_RPAREN,
12452 '(', 0, TOK_LPAREN,
12453 0
12454};
12455/* ptr to ")" */
12456#define endexpression &op_tokens[sizeof(op_tokens)-7]
12457
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012458static arith_t
12459arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012460{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012461 char arithval; /* Current character under analysis */
12462 operator lasttok, op;
12463 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012464
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012465 const char *p = endexpression;
12466 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012467
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012468 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012469
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012470 /* Stack of integers */
12471 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12472 * in any given correct or incorrect expression is left as an exercise to
12473 * the reader. */
12474 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12475 *numstackptr = numstack;
12476 /* Stack of operator tokens */
12477 operator *stack = alloca((datasizes) * sizeof(operator)),
12478 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012479
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012480 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12481 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012482
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012483 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012484 arithval = *expr;
12485 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012486 if (p == endexpression) {
12487 /* Null expression. */
12488 return 0;
12489 }
12490
12491 /* This is only reached after all tokens have been extracted from the
12492 * input stream. If there are still tokens on the operator stack, they
12493 * are to be applied in order. At the end, there should be a final
12494 * result on the integer stack */
12495
12496 if (expr != endexpression + 1) {
12497 /* If we haven't done so already, */
12498 /* append a closing right paren */
12499 expr = endexpression;
12500 /* and let the loop process it. */
12501 continue;
12502 }
12503 /* At this point, we're done with the expression. */
12504 if (numstackptr != numstack+1) {
12505 /* ... but if there isn't, it's bad */
12506 err:
12507 return (*perrcode = -1);
12508 }
12509 if (numstack->var) {
12510 /* expression is $((var)) only, lookup now */
12511 errcode = arith_lookup_val(numstack);
12512 }
12513 ret:
12514 *perrcode = errcode;
12515 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012516 }
12517
Eric Andersen90898442003-08-06 11:20:52 +000012518 /* Continue processing the expression. */
12519 if (arith_isspace(arithval)) {
12520 /* Skip whitespace */
12521 goto prologue;
12522 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012523 p = endofname(expr);
12524 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012525 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012526
12527 numstackptr->var = alloca(var_name_size);
12528 safe_strncpy(numstackptr->var, expr, var_name_size);
12529 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012530 num:
Eric Andersen90898442003-08-06 11:20:52 +000012531 numstackptr->contidional_second_val_initialized = 0;
12532 numstackptr++;
12533 lasttok = TOK_NUM;
12534 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012535 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012536 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012537 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012538#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012539 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012540#else
12541 numstackptr->val = strtol(expr, (char **) &expr, 0);
12542#endif
Eric Andersen90898442003-08-06 11:20:52 +000012543 goto num;
12544 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012545 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012546 const char *o;
12547
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012548 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012549 /* strange operator not found */
12550 goto err;
12551 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012552 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012553 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012554 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012555 /* found */
12556 expr = o - 1;
12557 break;
12558 }
12559 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012560 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012561 p++;
12562 /* skip zero delim */
12563 p++;
12564 }
12565 op = p[1];
12566
12567 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012568 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12569 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012570
12571 /* Plus and minus are binary (not unary) _only_ if the last
12572 * token was as number, or a right paren (which pretends to be
12573 * a number, since it evaluates to one). Think about it.
12574 * It makes sense. */
12575 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012576 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012577 case TOK_ADD:
12578 op = TOK_UPLUS;
12579 break;
12580 case TOK_SUB:
12581 op = TOK_UMINUS;
12582 break;
12583 case TOK_POST_INC:
12584 op = TOK_PRE_INC;
12585 break;
12586 case TOK_POST_DEC:
12587 op = TOK_PRE_DEC;
12588 break;
Eric Andersen90898442003-08-06 11:20:52 +000012589 }
12590 }
12591 /* We don't want a unary operator to cause recursive descent on the
12592 * stack, because there can be many in a row and it could cause an
12593 * operator to be evaluated before its argument is pushed onto the
12594 * integer stack. */
12595 /* But for binary operators, "apply" everything on the operator
12596 * stack until we find an operator with a lesser priority than the
12597 * one we have just extracted. */
12598 /* Left paren is given the lowest priority so it will never be
12599 * "applied" in this way.
12600 * if associativity is right and priority eq, applied also skip
12601 */
12602 prec = PREC(op);
12603 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12604 /* not left paren or unary */
12605 if (lasttok != TOK_NUM) {
12606 /* binary op must be preceded by a num */
12607 goto err;
12608 }
12609 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012610 if (op == TOK_RPAREN) {
12611 /* The algorithm employed here is simple: while we don't
12612 * hit an open paren nor the bottom of the stack, pop
12613 * tokens and apply them */
12614 if (stackptr[-1] == TOK_LPAREN) {
12615 --stackptr;
12616 /* Any operator directly after a */
12617 lasttok = TOK_NUM;
12618 /* close paren should consider itself binary */
12619 goto prologue;
12620 }
12621 } else {
12622 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012623
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012624 convert_prec_is_assing(prec);
12625 convert_prec_is_assing(prev_prec);
12626 if (prev_prec < prec)
12627 break;
12628 /* check right assoc */
12629 if (prev_prec == prec && is_right_associativity(prec))
12630 break;
12631 }
12632 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12633 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012634 }
12635 if (op == TOK_RPAREN) {
12636 goto err;
12637 }
12638 }
12639
12640 /* Push this operator to the stack and remember it. */
12641 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012642 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012643 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012644 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012645}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012646#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012647
12648
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012649/* ============ main() and helpers */
12650
12651/*
12652 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012653 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012654static void exitshell(void) ATTRIBUTE_NORETURN;
12655static void
12656exitshell(void)
12657{
12658 struct jmploc loc;
12659 char *p;
12660 int status;
12661
12662 status = exitstatus;
12663 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12664 if (setjmp(loc.loc)) {
12665 if (exception == EXEXIT)
12666/* dash bug: it just does _exit(exitstatus) here
12667 * but we have to do setjobctl(0) first!
12668 * (bug is still not fixed in dash-0.5.3 - if you run dash
12669 * under Midnight Commander, on exit from dash MC is backgrounded) */
12670 status = exitstatus;
12671 goto out;
12672 }
12673 exception_handler = &loc;
12674 p = trap[0];
12675 if (p) {
12676 trap[0] = NULL;
12677 evalstring(p, 0);
12678 }
12679 flush_stdout_stderr();
12680 out:
12681 setjobctl(0);
12682 _exit(status);
12683 /* NOTREACHED */
12684}
12685
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012686static void
12687init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012688{
12689 /* from input.c: */
12690 basepf.nextc = basepf.buf = basebuf;
12691
12692 /* from trap.c: */
12693 signal(SIGCHLD, SIG_DFL);
12694
12695 /* from var.c: */
12696 {
12697 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012698 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012699 const char *p;
12700 struct stat st1, st2;
12701
12702 initvar();
12703 for (envp = environ; envp && *envp; envp++) {
12704 if (strchr(*envp, '=')) {
12705 setvareq(*envp, VEXPORT|VTEXTFIXED);
12706 }
12707 }
12708
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012709 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012710 setvar("PPID", ppid, 0);
12711
12712 p = lookupvar("PWD");
12713 if (p)
12714 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12715 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12716 p = '\0';
12717 setpwd(p, 0);
12718 }
12719}
12720
12721/*
12722 * Process the shell command line arguments.
12723 */
12724static void
12725procargs(int argc, char **argv)
12726{
12727 int i;
12728 const char *xminusc;
12729 char **xargv;
12730
12731 xargv = argv;
12732 arg0 = xargv[0];
12733 if (argc > 0)
12734 xargv++;
12735 for (i = 0; i < NOPTS; i++)
12736 optlist[i] = 2;
12737 argptr = xargv;
12738 options(1);
12739 xargv = argptr;
12740 xminusc = minusc;
12741 if (*xargv == NULL) {
12742 if (xminusc)
12743 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12744 sflag = 1;
12745 }
12746 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12747 iflag = 1;
12748 if (mflag == 2)
12749 mflag = iflag;
12750 for (i = 0; i < NOPTS; i++)
12751 if (optlist[i] == 2)
12752 optlist[i] = 0;
12753#if DEBUG == 2
12754 debug = 1;
12755#endif
12756 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12757 if (xminusc) {
12758 minusc = *xargv++;
12759 if (*xargv)
12760 goto setarg0;
12761 } else if (!sflag) {
12762 setinputfile(*xargv, 0);
12763 setarg0:
12764 arg0 = *xargv++;
12765 commandname = arg0;
12766 }
12767
12768 shellparam.p = xargv;
12769#if ENABLE_ASH_GETOPTS
12770 shellparam.optind = 1;
12771 shellparam.optoff = -1;
12772#endif
12773 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12774 while (*xargv) {
12775 shellparam.nparam++;
12776 xargv++;
12777 }
12778 optschanged();
12779}
12780
12781/*
12782 * Read /etc/profile or .profile.
12783 */
12784static void
12785read_profile(const char *name)
12786{
12787 int skip;
12788
12789 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12790 return;
12791 skip = cmdloop(0);
12792 popfile();
12793 if (skip)
12794 exitshell();
12795}
12796
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012797/*
12798 * This routine is called when an error or an interrupt occurs in an
12799 * interactive shell and control is returned to the main command loop.
12800 */
12801static void
12802reset(void)
12803{
12804 /* from eval.c: */
12805 evalskip = 0;
12806 loopnest = 0;
12807 /* from input.c: */
12808 parselleft = parsenleft = 0; /* clear input buffer */
12809 popallfiles();
12810 /* from parser.c: */
12811 tokpushback = 0;
12812 checkkwd = 0;
12813 /* from redir.c: */
12814 clearredir(0);
12815}
12816
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012817#if PROFILE
12818static short profile_buf[16384];
12819extern int etext();
12820#endif
12821
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012822/*
12823 * Main routine. We initialize things, parse the arguments, execute
12824 * profiles if we're a login shell, and then call cmdloop to execute
12825 * commands. The setjmp call sets up the location to jump to when an
12826 * exception occurs. When an exception occurs the variable "state"
12827 * is used to figure out how far we had gotten.
12828 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012829int ash_main(int argc, char **argv);
12830int ash_main(int argc, char **argv)
12831{
12832 char *shinit;
12833 volatile int state;
12834 struct jmploc jmploc;
12835 struct stackmark smark;
12836
12837#ifdef __GLIBC__
12838 dash_errno = __errno_location();
12839#endif
12840
12841#if PROFILE
12842 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12843#endif
12844
12845#if ENABLE_FEATURE_EDITING
12846 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12847#endif
12848 state = 0;
12849 if (setjmp(jmploc.loc)) {
12850 int e;
12851 int s;
12852
12853 reset();
12854
12855 e = exception;
12856 if (e == EXERROR)
12857 exitstatus = 2;
12858 s = state;
12859 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12860 exitshell();
12861
12862 if (e == EXINT) {
12863 outcslow('\n', stderr);
12864 }
12865 popstackmark(&smark);
12866 FORCE_INT_ON; /* enable interrupts */
12867 if (s == 1)
12868 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012869 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012870 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012871 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012872 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012873 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012874 }
12875 exception_handler = &jmploc;
12876#if DEBUG
12877 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012878 trace_puts("Shell args: ");
12879 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012880#endif
12881 rootpid = getpid();
12882
12883#if ENABLE_ASH_RANDOM_SUPPORT
12884 rseed = rootpid + time(NULL);
12885#endif
12886 init();
12887 setstackmark(&smark);
12888 procargs(argc, argv);
12889#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12890 if (iflag) {
12891 const char *hp = lookupvar("HISTFILE");
12892
12893 if (hp == NULL) {
12894 hp = lookupvar("HOME");
12895 if (hp != NULL) {
12896 char *defhp = concat_path_file(hp, ".ash_history");
12897 setvar("HISTFILE", defhp, 0);
12898 free(defhp);
12899 }
12900 }
12901 }
12902#endif
12903 if (argv[0] && argv[0][0] == '-')
12904 isloginsh = 1;
12905 if (isloginsh) {
12906 state = 1;
12907 read_profile("/etc/profile");
12908 state1:
12909 state = 2;
12910 read_profile(".profile");
12911 }
12912 state2:
12913 state = 3;
12914 if (
12915#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012916 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012917#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012918 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012919 ) {
12920 shinit = lookupvar("ENV");
12921 if (shinit != NULL && *shinit != '\0') {
12922 read_profile(shinit);
12923 }
12924 }
12925 state3:
12926 state = 4;
12927 if (minusc)
12928 evalstring(minusc, 0);
12929
12930 if (sflag || minusc == NULL) {
12931#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12932 if ( iflag ) {
12933 const char *hp = lookupvar("HISTFILE");
12934
12935 if (hp != NULL)
12936 line_input_state->hist_file = hp;
12937 }
12938#endif
12939 state4: /* XXX ??? - why isn't this before the "if" statement */
12940 cmdloop(1);
12941 }
12942#if PROFILE
12943 monitor(0);
12944#endif
12945#ifdef GPROF
12946 {
12947 extern void _mcleanup(void);
12948 _mcleanup();
12949 }
12950#endif
12951 exitshell();
12952 /* NOTREACHED */
12953}
12954
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012955#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012956const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012957int main(int argc, char **argv)
12958{
12959 return ash_main(argc, argv);
12960}
12961#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012962
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012963
Eric Andersendf82f612001-06-28 07:46:40 +000012964/*-
12965 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012966 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012967 *
12968 * This code is derived from software contributed to Berkeley by
12969 * Kenneth Almquist.
12970 *
12971 * Redistribution and use in source and binary forms, with or without
12972 * modification, are permitted provided that the following conditions
12973 * are met:
12974 * 1. Redistributions of source code must retain the above copyright
12975 * notice, this list of conditions and the following disclaimer.
12976 * 2. Redistributions in binary form must reproduce the above copyright
12977 * notice, this list of conditions and the following disclaimer in the
12978 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012979 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012980 * may be used to endorse or promote products derived from this software
12981 * without specific prior written permission.
12982 *
12983 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12984 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12985 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12986 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12987 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12988 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12989 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12990 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12991 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12992 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12993 * SUCH DAMAGE.
12994 */