blob: 90936fcc0364a1f7608f116beb1cd6152aa90802 [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 */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000186static volatile sig_atomic_t pendingsig;
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;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000242 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000243
244 intpending = 0;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000245 /* Signal is not automatically re-enabled after it is raised,
246 * do it ourself */
247 sigemptyset(&mask);
248 sigprocmask(SIG_SETMASK, &mask, 0);
249 /* pendingsig = 0; - now done in onsig() */
250
Denis Vlasenkob012b102007-02-19 22:43:01 +0000251 i = EXSIG;
252 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
253 if (!(rootshell && iflag)) {
254 signal(SIGINT, SIG_DFL);
255 raise(SIGINT);
256 }
257 i = EXINT;
258 }
259 raise_exception(i);
260 /* NOTREACHED */
261}
262
263#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000264static void
265int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000266{
267 if (--suppressint == 0 && intpending) {
268 raise_interrupt();
269 }
270}
271#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000272static void
273force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000274{
275 suppressint = 0;
276 if (intpending)
277 raise_interrupt();
278}
279#define FORCE_INT_ON force_int_on()
280#else
281#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000282 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000283 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000284 if (--suppressint == 0 && intpending) \
285 raise_interrupt(); \
286 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000287#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000288 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000289 xbarrier(); \
290 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000291 if (intpending) \
292 raise_interrupt(); \
293 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000294#endif /* ASH_OPTIMIZE_FOR_SIZE */
295
296#define SAVE_INT(v) ((v) = suppressint)
297
298#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000299 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000300 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000301 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000302 if (suppressint == 0 && intpending) \
303 raise_interrupt(); \
304 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000305
306#define EXSIGON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000307 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000308 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000309 xbarrier(); \
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000310 if (pendingsig) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000311 raise_exception(EXSIG); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000312 } while (0)
Eric Andersenc470f442003-07-28 09:56:35 +0000313/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000314
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000315/*
316 * Ignore a signal. Only one usage site - in forkchild()
317 */
318static void
319ignoresig(int signo)
320{
321 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
322 signal(signo, SIG_IGN);
323 }
324 sigmode[signo - 1] = S_HARD_IGN;
325}
326
327/*
328 * Signal handler. Only one usage site - in setsignal()
329 */
330static void
331onsig(int signo)
332{
333 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000334 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000335
336 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000337 if (!suppressint) {
338 pendingsig = 0;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000339 raise_interrupt();
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000340 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000341 intpending = 1;
342 }
343}
344
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000345
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000346/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000347
Eric Andersenc470f442003-07-28 09:56:35 +0000348static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000349outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000350{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000351 INT_OFF;
352 fputs(p, file);
353 INT_ON;
354}
355
356static void
357flush_stdout_stderr(void)
358{
359 INT_OFF;
360 fflush(stdout);
361 fflush(stderr);
362 INT_ON;
363}
364
365static void
366flush_stderr(void)
367{
368 INT_OFF;
369 fflush(stderr);
370 INT_ON;
371}
372
373static void
374outcslow(int c, FILE *dest)
375{
376 INT_OFF;
377 putc(c, dest);
378 fflush(dest);
379 INT_ON;
380}
381
382static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
383static int
384out1fmt(const char *fmt, ...)
385{
386 va_list ap;
387 int r;
388
389 INT_OFF;
390 va_start(ap, fmt);
391 r = vprintf(fmt, ap);
392 va_end(ap);
393 INT_ON;
394 return r;
395}
396
397static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
398static int
399fmtstr(char *outbuf, size_t length, const char *fmt, ...)
400{
401 va_list ap;
402 int ret;
403
404 va_start(ap, fmt);
405 INT_OFF;
406 ret = vsnprintf(outbuf, length, fmt, ap);
407 va_end(ap);
408 INT_ON;
409 return ret;
410}
411
412static void
413out1str(const char *p)
414{
415 outstr(p, stdout);
416}
417
418static void
419out2str(const char *p)
420{
421 outstr(p, stderr);
422 flush_stderr();
423}
424
425
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000426/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000427
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000428/* control characters in argument strings */
429#define CTLESC '\201' /* escape next character */
430#define CTLVAR '\202' /* variable defn */
431#define CTLENDVAR '\203'
432#define CTLBACKQ '\204'
433#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
434/* CTLBACKQ | CTLQUOTE == '\205' */
435#define CTLARI '\206' /* arithmetic expression */
436#define CTLENDARI '\207'
437#define CTLQUOTEMARK '\210'
438
439/* variable substitution byte (follows CTLVAR) */
440#define VSTYPE 0x0f /* type of variable substitution */
441#define VSNUL 0x10 /* colon--treat the empty string as unset */
442#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
443
444/* values of VSTYPE field */
445#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
446#define VSMINUS 0x2 /* ${var-text} */
447#define VSPLUS 0x3 /* ${var+text} */
448#define VSQUESTION 0x4 /* ${var?message} */
449#define VSASSIGN 0x5 /* ${var=text} */
450#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
451#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
452#define VSTRIMLEFT 0x8 /* ${var#pattern} */
453#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
454#define VSLENGTH 0xa /* ${#var} */
455
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000456static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
457
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000458#define NCMD 0
459#define NPIPE 1
460#define NREDIR 2
461#define NBACKGND 3
462#define NSUBSHELL 4
463#define NAND 5
464#define NOR 6
465#define NSEMI 7
466#define NIF 8
467#define NWHILE 9
468#define NUNTIL 10
469#define NFOR 11
470#define NCASE 12
471#define NCLIST 13
472#define NDEFUN 14
473#define NARG 15
474#define NTO 16
475#define NCLOBBER 17
476#define NFROM 18
477#define NFROMTO 19
478#define NAPPEND 20
479#define NTOFD 21
480#define NFROMFD 22
481#define NHERE 23
482#define NXHERE 24
483#define NNOT 25
484
485union node;
486
487struct ncmd {
488 int type;
489 union node *assign;
490 union node *args;
491 union node *redirect;
492};
493
494struct npipe {
495 int type;
496 int backgnd;
497 struct nodelist *cmdlist;
498};
499
500struct nredir {
501 int type;
502 union node *n;
503 union node *redirect;
504};
505
506struct nbinary {
507 int type;
508 union node *ch1;
509 union node *ch2;
510};
511
512struct nif {
513 int type;
514 union node *test;
515 union node *ifpart;
516 union node *elsepart;
517};
518
519struct nfor {
520 int type;
521 union node *args;
522 union node *body;
523 char *var;
524};
525
526struct ncase {
527 int type;
528 union node *expr;
529 union node *cases;
530};
531
532struct nclist {
533 int type;
534 union node *next;
535 union node *pattern;
536 union node *body;
537};
538
539struct narg {
540 int type;
541 union node *next;
542 char *text;
543 struct nodelist *backquote;
544};
545
546struct nfile {
547 int type;
548 union node *next;
549 int fd;
550 union node *fname;
551 char *expfname;
552};
553
554struct ndup {
555 int type;
556 union node *next;
557 int fd;
558 int dupfd;
559 union node *vname;
560};
561
562struct nhere {
563 int type;
564 union node *next;
565 int fd;
566 union node *doc;
567};
568
569struct nnot {
570 int type;
571 union node *com;
572};
573
574union node {
575 int type;
576 struct ncmd ncmd;
577 struct npipe npipe;
578 struct nredir nredir;
579 struct nbinary nbinary;
580 struct nif nif;
581 struct nfor nfor;
582 struct ncase ncase;
583 struct nclist nclist;
584 struct narg narg;
585 struct nfile nfile;
586 struct ndup ndup;
587 struct nhere nhere;
588 struct nnot nnot;
589};
590
591struct nodelist {
592 struct nodelist *next;
593 union node *n;
594};
595
596struct funcnode {
597 int count;
598 union node n;
599};
600
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000601/*
602 * Free a parse tree.
603 */
604static void
605freefunc(struct funcnode *f)
606{
607 if (f && --f->count < 0)
608 free(f);
609}
610
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000611
612/* ============ Debugging output */
613
614#if DEBUG
615
616static FILE *tracefile;
617
618static void
619trace_printf(const char *fmt, ...)
620{
621 va_list va;
622
623 if (debug != 1)
624 return;
625 va_start(va, fmt);
626 vfprintf(tracefile, fmt, va);
627 va_end(va);
628}
629
630static void
631trace_vprintf(const char *fmt, va_list va)
632{
633 if (debug != 1)
634 return;
635 vfprintf(tracefile, fmt, va);
636}
637
638static void
639trace_puts(const char *s)
640{
641 if (debug != 1)
642 return;
643 fputs(s, tracefile);
644}
645
646static void
647trace_puts_quoted(char *s)
648{
649 char *p;
650 char c;
651
652 if (debug != 1)
653 return;
654 putc('"', tracefile);
655 for (p = s; *p; p++) {
656 switch (*p) {
657 case '\n': c = 'n'; goto backslash;
658 case '\t': c = 't'; goto backslash;
659 case '\r': c = 'r'; goto backslash;
660 case '"': c = '"'; goto backslash;
661 case '\\': c = '\\'; goto backslash;
662 case CTLESC: c = 'e'; goto backslash;
663 case CTLVAR: c = 'v'; goto backslash;
664 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
665 case CTLBACKQ: c = 'q'; goto backslash;
666 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
667 backslash:
668 putc('\\', tracefile);
669 putc(c, tracefile);
670 break;
671 default:
672 if (*p >= ' ' && *p <= '~')
673 putc(*p, tracefile);
674 else {
675 putc('\\', tracefile);
676 putc(*p >> 6 & 03, tracefile);
677 putc(*p >> 3 & 07, tracefile);
678 putc(*p & 07, tracefile);
679 }
680 break;
681 }
682 }
683 putc('"', tracefile);
684}
685
686static void
687trace_puts_args(char **ap)
688{
689 if (debug != 1)
690 return;
691 if (!*ap)
692 return;
693 while (1) {
694 trace_puts_quoted(*ap);
695 if (!*++ap) {
696 putc('\n', tracefile);
697 break;
698 }
699 putc(' ', tracefile);
700 }
701}
702
703static void
704opentrace(void)
705{
706 char s[100];
707#ifdef O_APPEND
708 int flags;
709#endif
710
711 if (debug != 1) {
712 if (tracefile)
713 fflush(tracefile);
714 /* leave open because libedit might be using it */
715 return;
716 }
717 strcpy(s, "./trace");
718 if (tracefile) {
719 if (!freopen(s, "a", tracefile)) {
720 fprintf(stderr, "Can't re-open %s\n", s);
721 debug = 0;
722 return;
723 }
724 } else {
725 tracefile = fopen(s, "a");
726 if (tracefile == NULL) {
727 fprintf(stderr, "Can't open %s\n", s);
728 debug = 0;
729 return;
730 }
731 }
732#ifdef O_APPEND
733 flags = fcntl(fileno(tracefile), F_GETFL, 0);
734 if (flags >= 0)
735 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
736#endif
737 setlinebuf(tracefile);
738 fputs("\nTracing started.\n", tracefile);
739}
740
741static void
742indent(int amount, char *pfx, FILE *fp)
743{
744 int i;
745
746 for (i = 0; i < amount; i++) {
747 if (pfx && i == amount - 1)
748 fputs(pfx, fp);
749 putc('\t', fp);
750 }
751}
752
753/* little circular references here... */
754static void shtree(union node *n, int ind, char *pfx, FILE *fp);
755
756static void
757sharg(union node *arg, FILE *fp)
758{
759 char *p;
760 struct nodelist *bqlist;
761 int subtype;
762
763 if (arg->type != NARG) {
764 out1fmt("<node type %d>\n", arg->type);
765 abort();
766 }
767 bqlist = arg->narg.backquote;
768 for (p = arg->narg.text; *p; p++) {
769 switch (*p) {
770 case CTLESC:
771 putc(*++p, fp);
772 break;
773 case CTLVAR:
774 putc('$', fp);
775 putc('{', fp);
776 subtype = *++p;
777 if (subtype == VSLENGTH)
778 putc('#', fp);
779
780 while (*p != '=')
781 putc(*p++, fp);
782
783 if (subtype & VSNUL)
784 putc(':', fp);
785
786 switch (subtype & VSTYPE) {
787 case VSNORMAL:
788 putc('}', fp);
789 break;
790 case VSMINUS:
791 putc('-', fp);
792 break;
793 case VSPLUS:
794 putc('+', fp);
795 break;
796 case VSQUESTION:
797 putc('?', fp);
798 break;
799 case VSASSIGN:
800 putc('=', fp);
801 break;
802 case VSTRIMLEFT:
803 putc('#', fp);
804 break;
805 case VSTRIMLEFTMAX:
806 putc('#', fp);
807 putc('#', fp);
808 break;
809 case VSTRIMRIGHT:
810 putc('%', fp);
811 break;
812 case VSTRIMRIGHTMAX:
813 putc('%', fp);
814 putc('%', fp);
815 break;
816 case VSLENGTH:
817 break;
818 default:
819 out1fmt("<subtype %d>", subtype);
820 }
821 break;
822 case CTLENDVAR:
823 putc('}', fp);
824 break;
825 case CTLBACKQ:
826 case CTLBACKQ|CTLQUOTE:
827 putc('$', fp);
828 putc('(', fp);
829 shtree(bqlist->n, -1, NULL, fp);
830 putc(')', fp);
831 break;
832 default:
833 putc(*p, fp);
834 break;
835 }
836 }
837}
838
839static void
840shcmd(union node *cmd, FILE *fp)
841{
842 union node *np;
843 int first;
844 const char *s;
845 int dftfd;
846
847 first = 1;
848 for (np = cmd->ncmd.args; np; np = np->narg.next) {
849 if (! first)
850 putchar(' ');
851 sharg(np, fp);
852 first = 0;
853 }
854 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
855 if (! first)
856 putchar(' ');
857 switch (np->nfile.type) {
858 case NTO: s = ">"; dftfd = 1; break;
859 case NCLOBBER: s = ">|"; dftfd = 1; break;
860 case NAPPEND: s = ">>"; dftfd = 1; break;
861 case NTOFD: s = ">&"; dftfd = 1; break;
862 case NFROM: s = "<"; dftfd = 0; break;
863 case NFROMFD: s = "<&"; dftfd = 0; break;
864 case NFROMTO: s = "<>"; dftfd = 0; break;
865 default: s = "*error*"; dftfd = 0; break;
866 }
867 if (np->nfile.fd != dftfd)
868 fprintf(fp, "%d", np->nfile.fd);
869 fputs(s, fp);
870 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
871 fprintf(fp, "%d", np->ndup.dupfd);
872 } else {
873 sharg(np->nfile.fname, fp);
874 }
875 first = 0;
876 }
877}
878
879static void
880shtree(union node *n, int ind, char *pfx, FILE *fp)
881{
882 struct nodelist *lp;
883 const char *s;
884
885 if (n == NULL)
886 return;
887
888 indent(ind, pfx, fp);
889 switch (n->type) {
890 case NSEMI:
891 s = "; ";
892 goto binop;
893 case NAND:
894 s = " && ";
895 goto binop;
896 case NOR:
897 s = " || ";
898 binop:
899 shtree(n->nbinary.ch1, ind, NULL, fp);
900 /* if (ind < 0) */
901 fputs(s, fp);
902 shtree(n->nbinary.ch2, ind, NULL, fp);
903 break;
904 case NCMD:
905 shcmd(n, fp);
906 if (ind >= 0)
907 putc('\n', fp);
908 break;
909 case NPIPE:
910 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
911 shcmd(lp->n, fp);
912 if (lp->next)
913 fputs(" | ", fp);
914 }
915 if (n->npipe.backgnd)
916 fputs(" &", fp);
917 if (ind >= 0)
918 putc('\n', fp);
919 break;
920 default:
921 fprintf(fp, "<node type %d>", n->type);
922 if (ind >= 0)
923 putc('\n', fp);
924 break;
925 }
926}
927
928static void
929showtree(union node *n)
930{
931 trace_puts("showtree called\n");
932 shtree(n, 1, NULL, stdout);
933}
934
935#define TRACE(param) trace_printf param
936#define TRACEV(param) trace_vprintf param
937
938#else
939
940#define TRACE(param)
941#define TRACEV(param)
942
943#endif /* DEBUG */
944
945
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000946/* ============ Parser data */
947
948/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000949 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
950 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000951struct strlist {
952 struct strlist *next;
953 char *text;
954};
955
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000956#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000957struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000958#endif
959
Denis Vlasenkob012b102007-02-19 22:43:01 +0000960struct strpush {
961 struct strpush *prev; /* preceding string on stack */
962 char *prevstring;
963 int prevnleft;
964#if ENABLE_ASH_ALIAS
965 struct alias *ap; /* if push was associated with an alias */
966#endif
967 char *string; /* remember the string since it may change */
968};
969
970struct parsefile {
971 struct parsefile *prev; /* preceding file on stack */
972 int linno; /* current line */
973 int fd; /* file descriptor (or -1 if string) */
974 int nleft; /* number of chars left in this line */
975 int lleft; /* number of chars left in this buffer */
976 char *nextc; /* next char in buffer */
977 char *buf; /* input buffer */
978 struct strpush *strpush; /* for pushing strings at this level */
979 struct strpush basestrpush; /* so pushing one is fast */
980};
981
982static struct parsefile basepf; /* top level input file */
983static struct parsefile *parsefile = &basepf; /* current input file */
984static int startlinno; /* line # where last token started */
985static char *commandname; /* currently executing command */
986static struct strlist *cmdenviron; /* environment for builtin command */
987static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000988
989
990/* ============ Message printing */
991
992static void
993ash_vmsg(const char *msg, va_list ap)
994{
995 fprintf(stderr, "%s: ", arg0);
996 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +0000997 if (strcmp(arg0, commandname))
998 fprintf(stderr, "%s: ", commandname);
999 if (!iflag || parsefile->fd)
1000 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001001 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001002 vfprintf(stderr, msg, ap);
1003 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001004}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001005
1006/*
1007 * Exverror is called to raise the error exception. If the second argument
1008 * is not NULL then error prints an error message using printf style
1009 * formatting. It then raises the error exception.
1010 */
1011static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1012static void
1013ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001014{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001015#if DEBUG
1016 if (msg) {
1017 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1018 TRACEV((msg, ap));
1019 TRACE(("\") pid=%d\n", getpid()));
1020 } else
1021 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1022 if (msg)
1023#endif
1024 ash_vmsg(msg, ap);
1025
1026 flush_stdout_stderr();
1027 raise_exception(cond);
1028 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001029}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001030
1031static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1032static void
1033ash_msg_and_raise_error(const char *msg, ...)
1034{
1035 va_list ap;
1036
1037 va_start(ap, msg);
1038 ash_vmsg_and_raise(EXERROR, msg, ap);
1039 /* NOTREACHED */
1040 va_end(ap);
1041}
1042
1043static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1044static void
1045ash_msg_and_raise(int cond, const char *msg, ...)
1046{
1047 va_list ap;
1048
1049 va_start(ap, msg);
1050 ash_vmsg_and_raise(cond, msg, ap);
1051 /* NOTREACHED */
1052 va_end(ap);
1053}
1054
1055/*
1056 * error/warning routines for external builtins
1057 */
1058static void
1059ash_msg(const char *fmt, ...)
1060{
1061 va_list ap;
1062
1063 va_start(ap, fmt);
1064 ash_vmsg(fmt, ap);
1065 va_end(ap);
1066}
1067
1068/*
1069 * Return a string describing an error. The returned string may be a
1070 * pointer to a static buffer that will be overwritten on the next call.
1071 * Action describes the operation that got the error.
1072 */
1073static const char *
1074errmsg(int e, const char *em)
1075{
1076 if (e == ENOENT || e == ENOTDIR) {
1077 return em;
1078 }
1079 return strerror(e);
1080}
1081
1082
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001083/* ============ Memory allocation */
1084
1085/*
1086 * It appears that grabstackstr() will barf with such alignments
1087 * because stalloc() will return a string allocated in a new stackblock.
1088 */
1089#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1090enum {
1091 /* Most machines require the value returned from malloc to be aligned
1092 * in some way. The following macro will get this right
1093 * on many machines. */
1094 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1095 /* Minimum size of a block */
1096 MINSIZE = SHELL_ALIGN(504),
1097};
1098
1099struct stack_block {
1100 struct stack_block *prev;
1101 char space[MINSIZE];
1102};
1103
1104struct stackmark {
1105 struct stack_block *stackp;
1106 char *stacknxt;
1107 size_t stacknleft;
1108 struct stackmark *marknext;
1109};
1110
1111static struct stack_block stackbase;
1112static struct stack_block *stackp = &stackbase;
1113static struct stackmark *markp;
1114static char *stacknxt = stackbase.space;
1115static size_t stacknleft = MINSIZE;
1116static char *sstrend = stackbase.space + MINSIZE;
1117static int herefd = -1;
1118
1119#define stackblock() ((void *)stacknxt)
1120#define stackblocksize() stacknleft
1121
1122static void *
1123ckrealloc(void * p, size_t nbytes)
1124{
1125 p = realloc(p, nbytes);
1126 if (!p)
1127 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1128 return p;
1129}
1130
1131static void *
1132ckmalloc(size_t nbytes)
1133{
1134 return ckrealloc(NULL, nbytes);
1135}
1136
1137/*
1138 * Make a copy of a string in safe storage.
1139 */
1140static char *
1141ckstrdup(const char *s)
1142{
1143 char *p = strdup(s);
1144 if (!p)
1145 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1146 return p;
1147}
1148
1149/*
1150 * Parse trees for commands are allocated in lifo order, so we use a stack
1151 * to make this more efficient, and also to avoid all sorts of exception
1152 * handling code to handle interrupts in the middle of a parse.
1153 *
1154 * The size 504 was chosen because the Ultrix malloc handles that size
1155 * well.
1156 */
1157static void *
1158stalloc(size_t nbytes)
1159{
1160 char *p;
1161 size_t aligned;
1162
1163 aligned = SHELL_ALIGN(nbytes);
1164 if (aligned > stacknleft) {
1165 size_t len;
1166 size_t blocksize;
1167 struct stack_block *sp;
1168
1169 blocksize = aligned;
1170 if (blocksize < MINSIZE)
1171 blocksize = MINSIZE;
1172 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1173 if (len < blocksize)
1174 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1175 INT_OFF;
1176 sp = ckmalloc(len);
1177 sp->prev = stackp;
1178 stacknxt = sp->space;
1179 stacknleft = blocksize;
1180 sstrend = stacknxt + blocksize;
1181 stackp = sp;
1182 INT_ON;
1183 }
1184 p = stacknxt;
1185 stacknxt += aligned;
1186 stacknleft -= aligned;
1187 return p;
1188}
1189
1190static void
1191stunalloc(void *p)
1192{
1193#if DEBUG
1194 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
1195 write(2, "stunalloc\n", 10);
1196 abort();
1197 }
1198#endif
1199 stacknleft += stacknxt - (char *)p;
1200 stacknxt = p;
1201}
1202
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001203/*
1204 * Like strdup but works with the ash stack.
1205 */
1206static char *
1207ststrdup(const char *p)
1208{
1209 size_t len = strlen(p) + 1;
1210 return memcpy(stalloc(len), p, len);
1211}
1212
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001213static void
1214setstackmark(struct stackmark *mark)
1215{
1216 mark->stackp = stackp;
1217 mark->stacknxt = stacknxt;
1218 mark->stacknleft = stacknleft;
1219 mark->marknext = markp;
1220 markp = mark;
1221}
1222
1223static void
1224popstackmark(struct stackmark *mark)
1225{
1226 struct stack_block *sp;
1227
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001228 if (!mark->stackp)
1229 return;
1230
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001231 INT_OFF;
1232 markp = mark->marknext;
1233 while (stackp != mark->stackp) {
1234 sp = stackp;
1235 stackp = sp->prev;
1236 free(sp);
1237 }
1238 stacknxt = mark->stacknxt;
1239 stacknleft = mark->stacknleft;
1240 sstrend = mark->stacknxt + mark->stacknleft;
1241 INT_ON;
1242}
1243
1244/*
1245 * When the parser reads in a string, it wants to stick the string on the
1246 * stack and only adjust the stack pointer when it knows how big the
1247 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1248 * of space on top of the stack and stackblocklen returns the length of
1249 * this block. Growstackblock will grow this space by at least one byte,
1250 * possibly moving it (like realloc). Grabstackblock actually allocates the
1251 * part of the block that has been used.
1252 */
1253static void
1254growstackblock(void)
1255{
1256 size_t newlen;
1257
1258 newlen = stacknleft * 2;
1259 if (newlen < stacknleft)
1260 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1261 if (newlen < 128)
1262 newlen += 128;
1263
1264 if (stacknxt == stackp->space && stackp != &stackbase) {
1265 struct stack_block *oldstackp;
1266 struct stackmark *xmark;
1267 struct stack_block *sp;
1268 struct stack_block *prevstackp;
1269 size_t grosslen;
1270
1271 INT_OFF;
1272 oldstackp = stackp;
1273 sp = stackp;
1274 prevstackp = sp->prev;
1275 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1276 sp = ckrealloc(sp, grosslen);
1277 sp->prev = prevstackp;
1278 stackp = sp;
1279 stacknxt = sp->space;
1280 stacknleft = newlen;
1281 sstrend = sp->space + newlen;
1282
1283 /*
1284 * Stack marks pointing to the start of the old block
1285 * must be relocated to point to the new block
1286 */
1287 xmark = markp;
1288 while (xmark != NULL && xmark->stackp == oldstackp) {
1289 xmark->stackp = stackp;
1290 xmark->stacknxt = stacknxt;
1291 xmark->stacknleft = stacknleft;
1292 xmark = xmark->marknext;
1293 }
1294 INT_ON;
1295 } else {
1296 char *oldspace = stacknxt;
1297 int oldlen = stacknleft;
1298 char *p = stalloc(newlen);
1299
1300 /* free the space we just allocated */
1301 stacknxt = memcpy(p, oldspace, oldlen);
1302 stacknleft += newlen;
1303 }
1304}
1305
1306static void
1307grabstackblock(size_t len)
1308{
1309 len = SHELL_ALIGN(len);
1310 stacknxt += len;
1311 stacknleft -= len;
1312}
1313
1314/*
1315 * The following routines are somewhat easier to use than the above.
1316 * The user declares a variable of type STACKSTR, which may be declared
1317 * to be a register. The macro STARTSTACKSTR initializes things. Then
1318 * the user uses the macro STPUTC to add characters to the string. In
1319 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1320 * grown as necessary. When the user is done, she can just leave the
1321 * string there and refer to it using stackblock(). Or she can allocate
1322 * the space for it using grabstackstr(). If it is necessary to allow
1323 * someone else to use the stack temporarily and then continue to grow
1324 * the string, the user should use grabstack to allocate the space, and
1325 * then call ungrabstr(p) to return to the previous mode of operation.
1326 *
1327 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1328 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1329 * is space for at least one character.
1330 */
1331static void *
1332growstackstr(void)
1333{
1334 size_t len = stackblocksize();
1335 if (herefd >= 0 && len >= 1024) {
1336 full_write(herefd, stackblock(), len);
1337 return stackblock();
1338 }
1339 growstackblock();
1340 return stackblock() + len;
1341}
1342
1343/*
1344 * Called from CHECKSTRSPACE.
1345 */
1346static char *
1347makestrspace(size_t newlen, char *p)
1348{
1349 size_t len = p - stacknxt;
1350 size_t size = stackblocksize();
1351
1352 for (;;) {
1353 size_t nleft;
1354
1355 size = stackblocksize();
1356 nleft = size - len;
1357 if (nleft >= newlen)
1358 break;
1359 growstackblock();
1360 }
1361 return stackblock() + len;
1362}
1363
1364static char *
1365stack_nputstr(const char *s, size_t n, char *p)
1366{
1367 p = makestrspace(n, p);
1368 p = memcpy(p, s, n) + n;
1369 return p;
1370}
1371
1372static char *
1373stack_putstr(const char *s, char *p)
1374{
1375 return stack_nputstr(s, strlen(s), p);
1376}
1377
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001378static char *
1379_STPUTC(int c, char *p)
1380{
1381 if (p == sstrend)
1382 p = growstackstr();
1383 *p++ = c;
1384 return p;
1385}
1386
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001387#define STARTSTACKSTR(p) ((p) = stackblock())
1388#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001389#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001390 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001391 char *q = (p); \
1392 size_t l = (n); \
1393 size_t m = sstrend - q; \
1394 if (l > m) \
1395 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001396 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001397#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001398#define STACKSTRNUL(p) \
1399 do { \
1400 if ((p) == sstrend) \
1401 p = growstackstr(); \
1402 *p = '\0'; \
1403 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001404#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001405#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001406#define STADJUST(amount, p) (p += (amount))
1407
1408#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1409#define ungrabstackstr(s, p) stunalloc((s))
1410#define stackstrend() ((void *)sstrend)
1411
1412
1413/* ============ String helpers */
1414
1415/*
1416 * prefix -- see if pfx is a prefix of string.
1417 */
1418static char *
1419prefix(const char *string, const char *pfx)
1420{
1421 while (*pfx) {
1422 if (*pfx++ != *string++)
1423 return 0;
1424 }
1425 return (char *) string;
1426}
1427
1428/*
1429 * Check for a valid number. This should be elsewhere.
1430 */
1431static int
1432is_number(const char *p)
1433{
1434 do {
1435 if (!isdigit(*p))
1436 return 0;
1437 } while (*++p != '\0');
1438 return 1;
1439}
1440
1441/*
1442 * Convert a string of digits to an integer, printing an error message on
1443 * failure.
1444 */
1445static int
1446number(const char *s)
1447{
1448 if (!is_number(s))
1449 ash_msg_and_raise_error(illnum, s);
1450 return atoi(s);
1451}
1452
1453/*
1454 * Produce a possibly single quoted string suitable as input to the shell.
1455 * The return string is allocated on the stack.
1456 */
1457static char *
1458single_quote(const char *s)
1459{
1460 char *p;
1461
1462 STARTSTACKSTR(p);
1463
1464 do {
1465 char *q;
1466 size_t len;
1467
1468 len = strchrnul(s, '\'') - s;
1469
1470 q = p = makestrspace(len + 3, p);
1471
1472 *q++ = '\'';
1473 q = memcpy(q, s, len) + len;
1474 *q++ = '\'';
1475 s += len;
1476
1477 STADJUST(q - p, p);
1478
1479 len = strspn(s, "'");
1480 if (!len)
1481 break;
1482
1483 q = p = makestrspace(len + 3, p);
1484
1485 *q++ = '"';
1486 q = memcpy(q, s, len) + len;
1487 *q++ = '"';
1488 s += len;
1489
1490 STADJUST(q - p, p);
1491 } while (*s);
1492
1493 USTPUTC(0, p);
1494
1495 return stackblock();
1496}
1497
1498
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001499/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001500
1501static char **argptr; /* argument list for builtin commands */
1502static char *optionarg; /* set by nextopt (like getopt) */
1503static char *optptr; /* used by nextopt */
1504
1505/*
1506 * XXX - should get rid of. have all builtins use getopt(3). the
1507 * library getopt must have the BSD extension static variable "optreset"
1508 * otherwise it can't be used within the shell safely.
1509 *
1510 * Standard option processing (a la getopt) for builtin routines. The
1511 * only argument that is passed to nextopt is the option string; the
1512 * other arguments are unnecessary. It return the character, or '\0' on
1513 * end of input.
1514 */
1515static int
1516nextopt(const char *optstring)
1517{
1518 char *p;
1519 const char *q;
1520 char c;
1521
1522 p = optptr;
1523 if (p == NULL || *p == '\0') {
1524 p = *argptr;
1525 if (p == NULL || *p != '-' || *++p == '\0')
1526 return '\0';
1527 argptr++;
1528 if (LONE_DASH(p)) /* check for "--" */
1529 return '\0';
1530 }
1531 c = *p++;
1532 for (q = optstring; *q != c; ) {
1533 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001534 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001535 if (*++q == ':')
1536 q++;
1537 }
1538 if (*++q == ':') {
1539 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001540 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001541 optionarg = p;
1542 p = NULL;
1543 }
1544 optptr = p;
1545 return c;
1546}
1547
1548
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001549/* ============ Math support definitions */
1550
1551#if ENABLE_ASH_MATH_SUPPORT_64
1552typedef int64_t arith_t;
1553#define arith_t_type long long
1554#else
1555typedef long arith_t;
1556#define arith_t_type long
1557#endif
1558
1559#if ENABLE_ASH_MATH_SUPPORT
1560static arith_t dash_arith(const char *);
1561static arith_t arith(const char *expr, int *perrcode);
1562#endif
1563
1564#if ENABLE_ASH_RANDOM_SUPPORT
1565static unsigned long rseed;
1566#ifndef DYNAMIC_VAR
1567#define DYNAMIC_VAR
1568#endif
1569#endif
1570
1571
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001572/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001573
1574/* flags */
1575#define VEXPORT 0x01 /* variable is exported */
1576#define VREADONLY 0x02 /* variable cannot be modified */
1577#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1578#define VTEXTFIXED 0x08 /* text is statically allocated */
1579#define VSTACK 0x10 /* text is allocated on the stack */
1580#define VUNSET 0x20 /* the variable is not set */
1581#define VNOFUNC 0x40 /* don't call the callback function */
1582#define VNOSET 0x80 /* do not set variable - just readonly test */
1583#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1584#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001585# define VDYNAMIC 0x200 /* dynamic variable */
1586#else
1587# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001588#endif
1589
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001590static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1591#ifdef IFS_BROKEN
1592static const char defifsvar[] = "IFS= \t\n";
1593#define defifs (defifsvar + 4)
1594#else
1595static const char defifs[] = " \t\n";
1596#endif
1597
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001598struct shparam {
1599 int nparam; /* # of positional parameters (without $0) */
1600 unsigned char malloc; /* if parameter list dynamically allocated */
1601 char **p; /* parameter list */
1602#if ENABLE_ASH_GETOPTS
1603 int optind; /* next parameter to be processed by getopts */
1604 int optoff; /* used by getopts */
1605#endif
1606};
1607
1608static struct shparam shellparam; /* $@ current positional parameters */
1609
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00001610/*
1611 * Free the list of positional parameters.
1612 */
1613static void
1614freeparam(volatile struct shparam *param)
1615{
1616 char **ap;
1617
1618 if (param->malloc) {
1619 for (ap = param->p; *ap; ap++)
1620 free(*ap);
1621 free(param->p);
1622 }
1623}
1624
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001625#if ENABLE_ASH_GETOPTS
1626static void
1627getoptsreset(const char *value)
1628{
1629 shellparam.optind = number(value);
1630 shellparam.optoff = -1;
1631}
1632#endif
1633
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001634struct var {
1635 struct var *next; /* next entry in hash list */
1636 int flags; /* flags are defined above */
1637 const char *text; /* name=value */
1638 void (*func)(const char *); /* function to be called when */
1639 /* the variable gets set/unset */
1640};
1641
1642struct localvar {
1643 struct localvar *next; /* next local variable in list */
1644 struct var *vp; /* the variable that was made local */
1645 int flags; /* saved flags */
1646 const char *text; /* saved text */
1647};
1648
1649/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001650#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001651static void
1652change_lc_all(const char *value)
1653{
1654 if (value && *value != '\0')
1655 setlocale(LC_ALL, value);
1656}
1657static void
1658change_lc_ctype(const char *value)
1659{
1660 if (value && *value != '\0')
1661 setlocale(LC_CTYPE, value);
1662}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001663#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001664#if ENABLE_ASH_MAIL
1665static void chkmail(void);
1666static void changemail(const char *);
1667#endif
1668static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001669#if ENABLE_ASH_RANDOM_SUPPORT
1670static void change_random(const char *);
1671#endif
1672
1673static struct var varinit[] = {
1674#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001675 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001676#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001677 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001678#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001679#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001680 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1681 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001682#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001683 { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1684 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1685 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1686 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001687#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001688 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001689#endif
1690#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001691 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001692#endif
1693#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001694 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1695 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001696#endif
1697#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001698 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001699#endif
1700};
1701
1702#define vifs varinit[0]
1703#if ENABLE_ASH_MAIL
1704#define vmail (&vifs)[1]
1705#define vmpath (&vmail)[1]
1706#else
1707#define vmpath vifs
1708#endif
1709#define vpath (&vmpath)[1]
1710#define vps1 (&vpath)[1]
1711#define vps2 (&vps1)[1]
1712#define vps4 (&vps2)[1]
1713#define voptind (&vps4)[1]
1714#if ENABLE_ASH_GETOPTS
1715#define vrandom (&voptind)[1]
1716#else
1717#define vrandom (&vps4)[1]
1718#endif
1719#define defpath (defpathvar + 5)
1720
1721/*
1722 * The following macros access the values of the above variables.
1723 * They have to skip over the name. They return the null string
1724 * for unset variables.
1725 */
1726#define ifsval() (vifs.text + 4)
1727#define ifsset() ((vifs.flags & VUNSET) == 0)
1728#define mailval() (vmail.text + 5)
1729#define mpathval() (vmpath.text + 9)
1730#define pathval() (vpath.text + 5)
1731#define ps1val() (vps1.text + 4)
1732#define ps2val() (vps2.text + 4)
1733#define ps4val() (vps4.text + 4)
1734#define optindval() (voptind.text + 7)
1735
1736#define mpathset() ((vmpath.flags & VUNSET) == 0)
1737
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738/*
1739 * The parsefile structure pointed to by the global variable parsefile
1740 * contains information about the current file being read.
1741 */
1742struct redirtab {
1743 struct redirtab *next;
1744 int renamed[10];
1745 int nullredirs;
1746};
1747
1748static struct redirtab *redirlist;
1749static int nullredirs;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001750extern char **environ;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1752
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#define VTABSIZE 39
1754
1755static struct var *vartab[VTABSIZE];
1756
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1758#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1759
1760/*
1761 * Return of a legal variable name (a letter or underscore followed by zero or
1762 * more letters, underscores, and digits).
1763 */
1764static char *
1765endofname(const char *name)
1766{
1767 char *p;
1768
1769 p = (char *) name;
1770 if (!is_name(*p))
1771 return p;
1772 while (*++p) {
1773 if (!is_in_name(*p))
1774 break;
1775 }
1776 return p;
1777}
1778
1779/*
1780 * Compares two strings up to the first = or '\0'. The first
1781 * string must be terminated by '='; the second may be terminated by
1782 * either '=' or '\0'.
1783 */
1784static int
1785varcmp(const char *p, const char *q)
1786{
1787 int c, d;
1788
1789 while ((c = *p) == (d = *q)) {
1790 if (!c || c == '=')
1791 goto out;
1792 p++;
1793 q++;
1794 }
1795 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001796 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001797 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001798 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001799 out:
1800 return c - d;
1801}
1802
1803static int
1804varequal(const char *a, const char *b)
1805{
1806 return !varcmp(a, b);
1807}
1808
1809/*
1810 * Find the appropriate entry in the hash table from the name.
1811 */
1812static struct var **
1813hashvar(const char *p)
1814{
1815 unsigned hashval;
1816
1817 hashval = ((unsigned char) *p) << 4;
1818 while (*p && *p != '=')
1819 hashval += (unsigned char) *p++;
1820 return &vartab[hashval % VTABSIZE];
1821}
1822
1823static int
1824vpcmp(const void *a, const void *b)
1825{
1826 return varcmp(*(const char **)a, *(const char **)b);
1827}
1828
1829/*
1830 * This routine initializes the builtin variables.
1831 */
1832static void
1833initvar(void)
1834{
1835 struct var *vp;
1836 struct var *end;
1837 struct var **vpp;
1838
1839 /*
1840 * PS1 depends on uid
1841 */
1842#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1843 vps1.text = "PS1=\\w \\$ ";
1844#else
1845 if (!geteuid())
1846 vps1.text = "PS1=# ";
1847#endif
1848 vp = varinit;
1849 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1850 do {
1851 vpp = hashvar(vp->text);
1852 vp->next = *vpp;
1853 *vpp = vp;
1854 } while (++vp < end);
1855}
1856
1857static struct var **
1858findvar(struct var **vpp, const char *name)
1859{
1860 for (; *vpp; vpp = &(*vpp)->next) {
1861 if (varequal((*vpp)->text, name)) {
1862 break;
1863 }
1864 }
1865 return vpp;
1866}
1867
1868/*
1869 * Find the value of a variable. Returns NULL if not set.
1870 */
1871static char *
1872lookupvar(const char *name)
1873{
1874 struct var *v;
1875
1876 v = *findvar(hashvar(name), name);
1877 if (v) {
1878#ifdef DYNAMIC_VAR
1879 /*
1880 * Dynamic variables are implemented roughly the same way they are
1881 * in bash. Namely, they're "special" so long as they aren't unset.
1882 * As soon as they're unset, they're no longer dynamic, and dynamic
1883 * lookup will no longer happen at that point. -- PFM.
1884 */
1885 if ((v->flags & VDYNAMIC))
1886 (*v->func)(NULL);
1887#endif
1888 if (!(v->flags & VUNSET))
1889 return strchrnul(v->text, '=') + 1;
1890 }
1891 return NULL;
1892}
1893
1894/*
1895 * Search the environment of a builtin command.
1896 */
1897static char *
1898bltinlookup(const char *name)
1899{
1900 struct strlist *sp;
1901
1902 for (sp = cmdenviron; sp; sp = sp->next) {
1903 if (varequal(sp->text, name))
1904 return strchrnul(sp->text, '=') + 1;
1905 }
1906 return lookupvar(name);
1907}
1908
1909/*
1910 * Same as setvar except that the variable and value are passed in
1911 * the first argument as name=value. Since the first argument will
1912 * be actually stored in the table, it should not be a string that
1913 * will go away.
1914 * Called with interrupts off.
1915 */
1916static void
1917setvareq(char *s, int flags)
1918{
1919 struct var *vp, **vpp;
1920
1921 vpp = hashvar(s);
1922 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1923 vp = *findvar(vpp, s);
1924 if (vp) {
1925 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1926 const char *n;
1927
1928 if (flags & VNOSAVE)
1929 free(s);
1930 n = vp->text;
1931 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1932 }
1933
1934 if (flags & VNOSET)
1935 return;
1936
1937 if (vp->func && (flags & VNOFUNC) == 0)
1938 (*vp->func)(strchrnul(s, '=') + 1);
1939
1940 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1941 free((char*)vp->text);
1942
1943 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1944 } else {
1945 if (flags & VNOSET)
1946 return;
1947 /* not found */
1948 vp = ckmalloc(sizeof(*vp));
1949 vp->next = *vpp;
1950 vp->func = NULL;
1951 *vpp = vp;
1952 }
1953 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1954 s = ckstrdup(s);
1955 vp->text = s;
1956 vp->flags = flags;
1957}
1958
1959/*
1960 * Set the value of a variable. The flags argument is ored with the
1961 * flags of the variable. If val is NULL, the variable is unset.
1962 */
1963static void
1964setvar(const char *name, const char *val, int flags)
1965{
1966 char *p, *q;
1967 size_t namelen;
1968 char *nameeq;
1969 size_t vallen;
1970
1971 q = endofname(name);
1972 p = strchrnul(q, '=');
1973 namelen = p - name;
1974 if (!namelen || p != q)
1975 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1976 vallen = 0;
1977 if (val == NULL) {
1978 flags |= VUNSET;
1979 } else {
1980 vallen = strlen(val);
1981 }
1982 INT_OFF;
1983 nameeq = ckmalloc(namelen + vallen + 2);
1984 p = memcpy(nameeq, name, namelen) + namelen;
1985 if (val) {
1986 *p++ = '=';
1987 p = memcpy(p, val, vallen) + vallen;
1988 }
1989 *p = '\0';
1990 setvareq(nameeq, flags | VNOSAVE);
1991 INT_ON;
1992}
1993
1994#if ENABLE_ASH_GETOPTS
1995/*
1996 * Safe version of setvar, returns 1 on success 0 on failure.
1997 */
1998static int
1999setvarsafe(const char *name, const char *val, int flags)
2000{
2001 int err;
2002 volatile int saveint;
2003 struct jmploc *volatile savehandler = exception_handler;
2004 struct jmploc jmploc;
2005
2006 SAVE_INT(saveint);
2007 if (setjmp(jmploc.loc))
2008 err = 1;
2009 else {
2010 exception_handler = &jmploc;
2011 setvar(name, val, flags);
2012 err = 0;
2013 }
2014 exception_handler = savehandler;
2015 RESTORE_INT(saveint);
2016 return err;
2017}
2018#endif
2019
2020/*
2021 * Unset the specified variable.
2022 */
2023static int
2024unsetvar(const char *s)
2025{
2026 struct var **vpp;
2027 struct var *vp;
2028 int retval;
2029
2030 vpp = findvar(hashvar(s), s);
2031 vp = *vpp;
2032 retval = 2;
2033 if (vp) {
2034 int flags = vp->flags;
2035
2036 retval = 1;
2037 if (flags & VREADONLY)
2038 goto out;
2039#ifdef DYNAMIC_VAR
2040 vp->flags &= ~VDYNAMIC;
2041#endif
2042 if (flags & VUNSET)
2043 goto ok;
2044 if ((flags & VSTRFIXED) == 0) {
2045 INT_OFF;
2046 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2047 free((char*)vp->text);
2048 *vpp = vp->next;
2049 free(vp);
2050 INT_ON;
2051 } else {
2052 setvar(s, 0, 0);
2053 vp->flags &= ~VEXPORT;
2054 }
2055 ok:
2056 retval = 0;
2057 }
2058 out:
2059 return retval;
2060}
2061
2062/*
2063 * Process a linked list of variable assignments.
2064 */
2065static void
2066listsetvar(struct strlist *list_set_var, int flags)
2067{
2068 struct strlist *lp = list_set_var;
2069
2070 if (!lp)
2071 return;
2072 INT_OFF;
2073 do {
2074 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002075 lp = lp->next;
2076 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002077 INT_ON;
2078}
2079
2080/*
2081 * Generate a list of variables satisfying the given conditions.
2082 */
2083static char **
2084listvars(int on, int off, char ***end)
2085{
2086 struct var **vpp;
2087 struct var *vp;
2088 char **ep;
2089 int mask;
2090
2091 STARTSTACKSTR(ep);
2092 vpp = vartab;
2093 mask = on | off;
2094 do {
2095 for (vp = *vpp; vp; vp = vp->next) {
2096 if ((vp->flags & mask) == on) {
2097 if (ep == stackstrend())
2098 ep = growstackstr();
2099 *ep++ = (char *) vp->text;
2100 }
2101 }
2102 } while (++vpp < vartab + VTABSIZE);
2103 if (ep == stackstrend())
2104 ep = growstackstr();
2105 if (end)
2106 *end = ep;
2107 *ep++ = NULL;
2108 return grabstackstr(ep);
2109}
2110
2111
2112/* ============ Path search helper
2113 *
2114 * The variable path (passed by reference) should be set to the start
2115 * of the path before the first call; padvance will update
2116 * this value as it proceeds. Successive calls to padvance will return
2117 * the possible path expansions in sequence. If an option (indicated by
2118 * a percent sign) appears in the path entry then the global variable
2119 * pathopt will be set to point to it; otherwise pathopt will be set to
2120 * NULL.
2121 */
2122static const char *pathopt; /* set by padvance */
2123
2124static char *
2125padvance(const char **path, const char *name)
2126{
2127 const char *p;
2128 char *q;
2129 const char *start;
2130 size_t len;
2131
2132 if (*path == NULL)
2133 return NULL;
2134 start = *path;
2135 for (p = start; *p && *p != ':' && *p != '%'; p++);
2136 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2137 while (stackblocksize() < len)
2138 growstackblock();
2139 q = stackblock();
2140 if (p != start) {
2141 memcpy(q, start, p - start);
2142 q += p - start;
2143 *q++ = '/';
2144 }
2145 strcpy(q, name);
2146 pathopt = NULL;
2147 if (*p == '%') {
2148 pathopt = ++p;
2149 while (*p && *p != ':') p++;
2150 }
2151 if (*p == ':')
2152 *path = p + 1;
2153 else
2154 *path = NULL;
2155 return stalloc(len);
2156}
2157
2158
2159/* ============ Prompt */
2160
2161static int doprompt; /* if set, prompt the user */
2162static int needprompt; /* true if interactive and at start of line */
2163
2164#if ENABLE_FEATURE_EDITING
2165static line_input_t *line_input_state;
2166static const char *cmdedit_prompt;
2167static void
2168putprompt(const char *s)
2169{
2170 if (ENABLE_ASH_EXPAND_PRMT) {
2171 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002172 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002173 return;
2174 }
2175 cmdedit_prompt = s;
2176}
2177#else
2178static void
2179putprompt(const char *s)
2180{
2181 out2str(s);
2182}
2183#endif
2184
2185#if ENABLE_ASH_EXPAND_PRMT
2186/* expandstr() needs parsing machinery, so it is far away ahead... */
2187static const char *expandstr(const char *ps);
2188#else
2189#define expandstr(s) s
2190#endif
2191
2192static void
2193setprompt(int whichprompt)
2194{
2195 const char *prompt;
2196#if ENABLE_ASH_EXPAND_PRMT
2197 struct stackmark smark;
2198#endif
2199
2200 needprompt = 0;
2201
2202 switch (whichprompt) {
2203 case 1:
2204 prompt = ps1val();
2205 break;
2206 case 2:
2207 prompt = ps2val();
2208 break;
2209 default: /* 0 */
2210 prompt = nullstr;
2211 }
2212#if ENABLE_ASH_EXPAND_PRMT
2213 setstackmark(&smark);
2214 stalloc(stackblocksize());
2215#endif
2216 putprompt(expandstr(prompt));
2217#if ENABLE_ASH_EXPAND_PRMT
2218 popstackmark(&smark);
2219#endif
2220}
2221
2222
2223/* ============ The cd and pwd commands */
2224
2225#define CD_PHYSICAL 1
2226#define CD_PRINT 2
2227
2228static int docd(const char *, int);
2229
2230static char *curdir = nullstr; /* current working directory */
2231static char *physdir = nullstr; /* physical working directory */
2232
2233static int
2234cdopt(void)
2235{
2236 int flags = 0;
2237 int i, j;
2238
2239 j = 'L';
2240 while ((i = nextopt("LP"))) {
2241 if (i != j) {
2242 flags ^= CD_PHYSICAL;
2243 j = i;
2244 }
2245 }
2246
2247 return flags;
2248}
2249
2250/*
2251 * Update curdir (the name of the current directory) in response to a
2252 * cd command.
2253 */
2254static const char *
2255updatepwd(const char *dir)
2256{
2257 char *new;
2258 char *p;
2259 char *cdcomppath;
2260 const char *lim;
2261
2262 cdcomppath = ststrdup(dir);
2263 STARTSTACKSTR(new);
2264 if (*dir != '/') {
2265 if (curdir == nullstr)
2266 return 0;
2267 new = stack_putstr(curdir, new);
2268 }
2269 new = makestrspace(strlen(dir) + 2, new);
2270 lim = stackblock() + 1;
2271 if (*dir != '/') {
2272 if (new[-1] != '/')
2273 USTPUTC('/', new);
2274 if (new > lim && *lim == '/')
2275 lim++;
2276 } else {
2277 USTPUTC('/', new);
2278 cdcomppath++;
2279 if (dir[1] == '/' && dir[2] != '/') {
2280 USTPUTC('/', new);
2281 cdcomppath++;
2282 lim++;
2283 }
2284 }
2285 p = strtok(cdcomppath, "/");
2286 while (p) {
2287 switch (*p) {
2288 case '.':
2289 if (p[1] == '.' && p[2] == '\0') {
2290 while (new > lim) {
2291 STUNPUTC(new);
2292 if (new[-1] == '/')
2293 break;
2294 }
2295 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002296 }
2297 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002298 break;
2299 /* fall through */
2300 default:
2301 new = stack_putstr(p, new);
2302 USTPUTC('/', new);
2303 }
2304 p = strtok(0, "/");
2305 }
2306 if (new > lim)
2307 STUNPUTC(new);
2308 *new = 0;
2309 return stackblock();
2310}
2311
2312/*
2313 * Find out what the current directory is. If we already know the current
2314 * directory, this routine returns immediately.
2315 */
2316static char *
2317getpwd(void)
2318{
2319 char *dir = getcwd(0, 0);
2320 return dir ? dir : nullstr;
2321}
2322
2323static void
2324setpwd(const char *val, int setold)
2325{
2326 char *oldcur, *dir;
2327
2328 oldcur = dir = curdir;
2329
2330 if (setold) {
2331 setvar("OLDPWD", oldcur, VEXPORT);
2332 }
2333 INT_OFF;
2334 if (physdir != nullstr) {
2335 if (physdir != oldcur)
2336 free(physdir);
2337 physdir = nullstr;
2338 }
2339 if (oldcur == val || !val) {
2340 char *s = getpwd();
2341 physdir = s;
2342 if (!val)
2343 dir = s;
2344 } else
2345 dir = ckstrdup(val);
2346 if (oldcur != dir && oldcur != nullstr) {
2347 free(oldcur);
2348 }
2349 curdir = dir;
2350 INT_ON;
2351 setvar("PWD", dir, VEXPORT);
2352}
2353
2354static void hashcd(void);
2355
2356/*
2357 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2358 * know that the current directory has changed.
2359 */
2360static int
2361docd(const char *dest, int flags)
2362{
2363 const char *dir = 0;
2364 int err;
2365
2366 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2367
2368 INT_OFF;
2369 if (!(flags & CD_PHYSICAL)) {
2370 dir = updatepwd(dest);
2371 if (dir)
2372 dest = dir;
2373 }
2374 err = chdir(dest);
2375 if (err)
2376 goto out;
2377 setpwd(dir, 1);
2378 hashcd();
2379 out:
2380 INT_ON;
2381 return err;
2382}
2383
2384static int
2385cdcmd(int argc, char **argv)
2386{
2387 const char *dest;
2388 const char *path;
2389 const char *p;
2390 char c;
2391 struct stat statb;
2392 int flags;
2393
2394 flags = cdopt();
2395 dest = *argptr;
2396 if (!dest)
2397 dest = bltinlookup(homestr);
2398 else if (LONE_DASH(dest)) {
2399 dest = bltinlookup("OLDPWD");
2400 flags |= CD_PRINT;
2401 }
2402 if (!dest)
2403 dest = nullstr;
2404 if (*dest == '/')
2405 goto step7;
2406 if (*dest == '.') {
2407 c = dest[1];
2408 dotdot:
2409 switch (c) {
2410 case '\0':
2411 case '/':
2412 goto step6;
2413 case '.':
2414 c = dest[2];
2415 if (c != '.')
2416 goto dotdot;
2417 }
2418 }
2419 if (!*dest)
2420 dest = ".";
2421 path = bltinlookup("CDPATH");
2422 if (!path) {
2423 step6:
2424 step7:
2425 p = dest;
2426 goto docd;
2427 }
2428 do {
2429 c = *path;
2430 p = padvance(&path, dest);
2431 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2432 if (c && c != ':')
2433 flags |= CD_PRINT;
2434 docd:
2435 if (!docd(p, flags))
2436 goto out;
2437 break;
2438 }
2439 } while (path);
2440 ash_msg_and_raise_error("can't cd to %s", dest);
2441 /* NOTREACHED */
2442 out:
2443 if (flags & CD_PRINT)
2444 out1fmt(snlfmt, curdir);
2445 return 0;
2446}
2447
2448static int
2449pwdcmd(int argc, char **argv)
2450{
2451 int flags;
2452 const char *dir = curdir;
2453
2454 flags = cdopt();
2455 if (flags) {
2456 if (physdir == nullstr)
2457 setpwd(dir, 0);
2458 dir = physdir;
2459 }
2460 out1fmt(snlfmt, dir);
2461 return 0;
2462}
2463
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002464
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002465/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002466
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002467#define IBUFSIZ (BUFSIZ + 1)
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002468#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002469
Eric Andersenc470f442003-07-28 09:56:35 +00002470/* Syntax classes */
2471#define CWORD 0 /* character is nothing special */
2472#define CNL 1 /* newline character */
2473#define CBACK 2 /* a backslash character */
2474#define CSQUOTE 3 /* single quote */
2475#define CDQUOTE 4 /* double quote */
2476#define CENDQUOTE 5 /* a terminating quote */
2477#define CBQUOTE 6 /* backwards single quote */
2478#define CVAR 7 /* a dollar sign */
2479#define CENDVAR 8 /* a '}' character */
2480#define CLP 9 /* a left paren in arithmetic */
2481#define CRP 10 /* a right paren in arithmetic */
2482#define CENDFILE 11 /* end of file */
2483#define CCTL 12 /* like CWORD, except it must be escaped */
2484#define CSPCL 13 /* these terminate a word */
2485#define CIGN 14 /* character should be ignored */
2486
Denis Vlasenko131ae172007-02-18 13:00:19 +00002487#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002488#define SYNBASE 130
2489#define PEOF -130
2490#define PEOA -129
2491#define PEOA_OR_PEOF PEOA
2492#else
2493#define SYNBASE 129
2494#define PEOF -129
2495#define PEOA_OR_PEOF PEOF
2496#endif
2497
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002498/* number syntax index */
2499#define BASESYNTAX 0 /* not in quotes */
2500#define DQSYNTAX 1 /* in double quotes */
2501#define SQSYNTAX 2 /* in single quotes */
2502#define ARISYNTAX 3 /* in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002503
Denis Vlasenko131ae172007-02-18 13:00:19 +00002504#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002505#define USE_SIT_FUNCTION
2506#endif
2507
Denis Vlasenko131ae172007-02-18 13:00:19 +00002508#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002509static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002510#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002511 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002512#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002513 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2514 { CNL, CNL, CNL, CNL }, /* 2, \n */
2515 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2516 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2517 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2518 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2519 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2520 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2521 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2522 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2523 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002524#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002525 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2526 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2527 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002528#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002529};
Eric Andersenc470f442003-07-28 09:56:35 +00002530#else
2531static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002532#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002533 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002534#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002535 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2536 { CNL, CNL, CNL }, /* 2, \n */
2537 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2538 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2539 { CVAR, CVAR, CWORD }, /* 5, $ */
2540 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2541 { CSPCL, CWORD, CWORD }, /* 7, ( */
2542 { CSPCL, CWORD, CWORD }, /* 8, ) */
2543 { CBACK, CBACK, CCTL }, /* 9, \ */
2544 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2545 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002546#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002547 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2548 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2549 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002550#endif
2551};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002552#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002553
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002554#ifdef USE_SIT_FUNCTION
2555
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002556static int
2557SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002558{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002559 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002560#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002561 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002562 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2563 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2564 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2565 11, 3 /* "}~" */
2566 };
2567#else
2568 static const char syntax_index_table[] = {
2569 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2570 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2571 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2572 10, 2 /* "}~" */
2573 };
2574#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002575 const char *s;
2576 int indx;
2577
Eric Andersenc470f442003-07-28 09:56:35 +00002578 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002579 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002580#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002581 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002582 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002583 else
2584#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002585#define U_C(c) ((unsigned char)(c))
2586
2587 if ((unsigned char)c >= (unsigned char)(CTLESC)
2588 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2589 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002590 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002591 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002592 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002593 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002594 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002595 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002596 }
2597 return S_I_T[indx][syntax];
2598}
2599
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002600#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002601
Denis Vlasenko131ae172007-02-18 13:00:19 +00002602#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002603#define CSPCL_CIGN_CIGN_CIGN 0
2604#define CSPCL_CWORD_CWORD_CWORD 1
2605#define CNL_CNL_CNL_CNL 2
2606#define CWORD_CCTL_CCTL_CWORD 3
2607#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2608#define CVAR_CVAR_CWORD_CVAR 5
2609#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2610#define CSPCL_CWORD_CWORD_CLP 7
2611#define CSPCL_CWORD_CWORD_CRP 8
2612#define CBACK_CBACK_CCTL_CBACK 9
2613#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2614#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2615#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2616#define CWORD_CWORD_CWORD_CWORD 13
2617#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002618#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002619#define CSPCL_CWORD_CWORD_CWORD 0
2620#define CNL_CNL_CNL_CNL 1
2621#define CWORD_CCTL_CCTL_CWORD 2
2622#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2623#define CVAR_CVAR_CWORD_CVAR 4
2624#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2625#define CSPCL_CWORD_CWORD_CLP 6
2626#define CSPCL_CWORD_CWORD_CRP 7
2627#define CBACK_CBACK_CCTL_CBACK 8
2628#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2629#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2630#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2631#define CWORD_CWORD_CWORD_CWORD 12
2632#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002633#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002634
2635static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002636 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002637 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002638#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002639 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2640#endif
2641 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2642 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2643 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2644 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2645 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2646 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2647 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2648 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2649 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002650 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2779 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2780 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2802 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002803 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002804 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2805 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2806 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2807 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002808 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002809 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2810 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2811 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2812 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2813 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2814 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2815 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2816 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2817 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2818 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2819 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2820 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2821 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2822 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2823 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2824 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2825 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2826 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2827 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2828 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2829 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2830 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2831 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2832 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2833 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2851 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2852 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2853 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2854 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2855 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2856 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2857 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2858 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2859 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2860 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2861 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2862 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2863 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2865 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2866 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2894 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2895 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2896 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002897};
2898
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002899#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2900
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002901#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002902
Eric Andersen2870d962001-07-02 17:27:21 +00002903
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002904/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002905
Denis Vlasenko131ae172007-02-18 13:00:19 +00002906#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002907
2908#define ALIASINUSE 1
2909#define ALIASDEAD 2
2910
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002911#define ATABSIZE 39
2912
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002913struct alias {
2914 struct alias *next;
2915 char *name;
2916 char *val;
2917 int flag;
2918};
2919
Eric Andersen2870d962001-07-02 17:27:21 +00002920static struct alias *atab[ATABSIZE];
2921
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002922static struct alias **
2923__lookupalias(const char *name) {
2924 unsigned int hashval;
2925 struct alias **app;
2926 const char *p;
2927 unsigned int ch;
2928
2929 p = name;
2930
2931 ch = (unsigned char)*p;
2932 hashval = ch << 4;
2933 while (ch) {
2934 hashval += ch;
2935 ch = (unsigned char)*++p;
2936 }
2937 app = &atab[hashval % ATABSIZE];
2938
2939 for (; *app; app = &(*app)->next) {
2940 if (strcmp(name, (*app)->name) == 0) {
2941 break;
2942 }
2943 }
2944
2945 return app;
2946}
2947
2948static struct alias *
2949lookupalias(const char *name, int check)
2950{
2951 struct alias *ap = *__lookupalias(name);
2952
2953 if (check && ap && (ap->flag & ALIASINUSE))
2954 return NULL;
2955 return ap;
2956}
2957
2958static struct alias *
2959freealias(struct alias *ap)
2960{
2961 struct alias *next;
2962
2963 if (ap->flag & ALIASINUSE) {
2964 ap->flag |= ALIASDEAD;
2965 return ap;
2966 }
2967
2968 next = ap->next;
2969 free(ap->name);
2970 free(ap->val);
2971 free(ap);
2972 return next;
2973}
Eric Andersencb57d552001-06-28 07:25:16 +00002974
Eric Andersenc470f442003-07-28 09:56:35 +00002975static void
2976setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002977{
2978 struct alias *ap, **app;
2979
2980 app = __lookupalias(name);
2981 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002982 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002983 if (ap) {
2984 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002985 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002986 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002987 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002988 ap->flag &= ~ALIASDEAD;
2989 } else {
2990 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002991 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002992 ap->name = ckstrdup(name);
2993 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002994 ap->flag = 0;
2995 ap->next = 0;
2996 *app = ap;
2997 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002998 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002999}
3000
Eric Andersenc470f442003-07-28 09:56:35 +00003001static int
3002unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003003{
Eric Andersencb57d552001-06-28 07:25:16 +00003004 struct alias **app;
3005
3006 app = __lookupalias(name);
3007
3008 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003009 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003010 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003011 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003012 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003013 }
3014
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003015 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003016}
3017
Eric Andersenc470f442003-07-28 09:56:35 +00003018static void
3019rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003020{
Eric Andersencb57d552001-06-28 07:25:16 +00003021 struct alias *ap, **app;
3022 int i;
3023
Denis Vlasenkob012b102007-02-19 22:43:01 +00003024 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003025 for (i = 0; i < ATABSIZE; i++) {
3026 app = &atab[i];
3027 for (ap = *app; ap; ap = *app) {
3028 *app = freealias(*app);
3029 if (ap == *app) {
3030 app = &ap->next;
3031 }
3032 }
3033 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003034 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003035}
3036
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003037static void
3038printalias(const struct alias *ap)
3039{
3040 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3041}
3042
Eric Andersencb57d552001-06-28 07:25:16 +00003043/*
3044 * TODO - sort output
3045 */
Eric Andersenc470f442003-07-28 09:56:35 +00003046static int
3047aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003048{
3049 char *n, *v;
3050 int ret = 0;
3051 struct alias *ap;
3052
3053 if (argc == 1) {
3054 int i;
3055
3056 for (i = 0; i < ATABSIZE; i++)
3057 for (ap = atab[i]; ap; ap = ap->next) {
3058 printalias(ap);
3059 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003060 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003061 }
3062 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003063 v = strchr(n+1, '=');
3064 if (v == NULL) { /* n+1: funny ksh stuff */
3065 ap = *__lookupalias(n);
3066 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003067 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003068 ret = 1;
3069 } else
3070 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003071 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003072 *v++ = '\0';
3073 setalias(n, v);
3074 }
3075 }
3076
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003077 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003078}
3079
Eric Andersenc470f442003-07-28 09:56:35 +00003080static int
3081unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003082{
3083 int i;
3084
3085 while ((i = nextopt("a")) != '\0') {
3086 if (i == 'a') {
3087 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003088 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003089 }
3090 }
3091 for (i = 0; *argptr; argptr++) {
3092 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003093 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003094 i = 1;
3095 }
3096 }
3097
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003098 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003099}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003100
Denis Vlasenko131ae172007-02-18 13:00:19 +00003101#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003102
Eric Andersenc470f442003-07-28 09:56:35 +00003103
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003104/* ============ jobs.c */
3105
3106/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3107#define FORK_FG 0
3108#define FORK_BG 1
3109#define FORK_NOJOB 2
3110
3111/* mode flags for showjob(s) */
3112#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3113#define SHOW_PID 0x04 /* include process pid */
3114#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3115
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003116/*
3117 * A job structure contains information about a job. A job is either a
3118 * single process or a set of processes contained in a pipeline. In the
3119 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3120 * array of pids.
3121 */
3122
3123struct procstat {
3124 pid_t pid; /* process id */
3125 int status; /* last process status from wait() */
3126 char *cmd; /* text of command being run */
3127};
3128
3129struct job {
3130 struct procstat ps0; /* status of process */
3131 struct procstat *ps; /* status or processes when more than one */
3132#if JOBS
3133 int stopstatus; /* status of a stopped job */
3134#endif
3135 uint32_t
3136 nprocs: 16, /* number of processes */
3137 state: 8,
3138#define JOBRUNNING 0 /* at least one proc running */
3139#define JOBSTOPPED 1 /* all procs are stopped */
3140#define JOBDONE 2 /* all procs are completed */
3141#if JOBS
3142 sigint: 1, /* job was killed by SIGINT */
3143 jobctl: 1, /* job running under job control */
3144#endif
3145 waited: 1, /* true if this entry has been waited for */
3146 used: 1, /* true if this entry is in used */
3147 changed: 1; /* true if status has changed */
3148 struct job *prev_job; /* previous job */
3149};
3150
3151static pid_t backgndpid; /* pid of last background process */
3152static int job_warning; /* user was warned about stopped jobs */
3153#if JOBS
3154static int jobctl; /* true if doing job control */
3155#endif
3156
3157static struct job *makejob(union node *, int);
3158static int forkshell(struct job *, union node *, int);
3159static int waitforjob(struct job *);
3160
3161#if ! JOBS
3162#define setjobctl(on) /* do nothing */
3163#else
3164static void setjobctl(int);
3165static void showjobs(FILE *, int);
3166#endif
3167
3168/*
3169 * Set the signal handler for the specified signal. The routine figures
3170 * out what it should be set to.
3171 */
3172static void
3173setsignal(int signo)
3174{
3175 int action;
3176 char *t, tsig;
3177 struct sigaction act;
3178
3179 t = trap[signo];
3180 if (t == NULL)
3181 action = S_DFL;
3182 else if (*t != '\0')
3183 action = S_CATCH;
3184 else
3185 action = S_IGN;
3186 if (rootshell && action == S_DFL) {
3187 switch (signo) {
3188 case SIGINT:
3189 if (iflag || minusc || sflag == 0)
3190 action = S_CATCH;
3191 break;
3192 case SIGQUIT:
3193#if DEBUG
3194 if (debug)
3195 break;
3196#endif
3197 /* FALLTHROUGH */
3198 case SIGTERM:
3199 if (iflag)
3200 action = S_IGN;
3201 break;
3202#if JOBS
3203 case SIGTSTP:
3204 case SIGTTOU:
3205 if (mflag)
3206 action = S_IGN;
3207 break;
3208#endif
3209 }
3210 }
3211
3212 t = &sigmode[signo - 1];
3213 tsig = *t;
3214 if (tsig == 0) {
3215 /*
3216 * current setting unknown
3217 */
3218 if (sigaction(signo, 0, &act) == -1) {
3219 /*
3220 * Pretend it worked; maybe we should give a warning
3221 * here, but other shells don't. We don't alter
3222 * sigmode, so that we retry every time.
3223 */
3224 return;
3225 }
3226 if (act.sa_handler == SIG_IGN) {
3227 if (mflag
3228 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3229 ) {
3230 tsig = S_IGN; /* don't hard ignore these */
3231 } else
3232 tsig = S_HARD_IGN;
3233 } else {
3234 tsig = S_RESET; /* force to be set */
3235 }
3236 }
3237 if (tsig == S_HARD_IGN || tsig == action)
3238 return;
3239 switch (action) {
3240 case S_CATCH:
3241 act.sa_handler = onsig;
3242 break;
3243 case S_IGN:
3244 act.sa_handler = SIG_IGN;
3245 break;
3246 default:
3247 act.sa_handler = SIG_DFL;
3248 }
3249 *t = action;
3250 act.sa_flags = 0;
3251 sigfillset(&act.sa_mask);
3252 sigaction(signo, &act, 0);
3253}
3254
3255/* mode flags for set_curjob */
3256#define CUR_DELETE 2
3257#define CUR_RUNNING 1
3258#define CUR_STOPPED 0
3259
3260/* mode flags for dowait */
3261#define DOWAIT_NORMAL 0
3262#define DOWAIT_BLOCK 1
3263
3264#if JOBS
3265/* pgrp of shell on invocation */
3266static int initialpgrp;
3267static int ttyfd = -1;
3268#endif
3269/* array of jobs */
3270static struct job *jobtab;
3271/* size of array */
3272static unsigned njobs;
3273/* current job */
3274static struct job *curjob;
3275/* number of presumed living untracked jobs */
3276static int jobless;
3277
3278static void
3279set_curjob(struct job *jp, unsigned mode)
3280{
3281 struct job *jp1;
3282 struct job **jpp, **curp;
3283
3284 /* first remove from list */
3285 jpp = curp = &curjob;
3286 do {
3287 jp1 = *jpp;
3288 if (jp1 == jp)
3289 break;
3290 jpp = &jp1->prev_job;
3291 } while (1);
3292 *jpp = jp1->prev_job;
3293
3294 /* Then re-insert in correct position */
3295 jpp = curp;
3296 switch (mode) {
3297 default:
3298#if DEBUG
3299 abort();
3300#endif
3301 case CUR_DELETE:
3302 /* job being deleted */
3303 break;
3304 case CUR_RUNNING:
3305 /* newly created job or backgrounded job,
3306 put after all stopped jobs. */
3307 do {
3308 jp1 = *jpp;
3309#if JOBS
3310 if (!jp1 || jp1->state != JOBSTOPPED)
3311#endif
3312 break;
3313 jpp = &jp1->prev_job;
3314 } while (1);
3315 /* FALLTHROUGH */
3316#if JOBS
3317 case CUR_STOPPED:
3318#endif
3319 /* newly stopped job - becomes curjob */
3320 jp->prev_job = *jpp;
3321 *jpp = jp;
3322 break;
3323 }
3324}
3325
3326#if JOBS || DEBUG
3327static int
3328jobno(const struct job *jp)
3329{
3330 return jp - jobtab + 1;
3331}
3332#endif
3333
3334/*
3335 * Convert a job name to a job structure.
3336 */
3337static struct job *
3338getjob(const char *name, int getctl)
3339{
3340 struct job *jp;
3341 struct job *found;
3342 const char *err_msg = "No such job: %s";
3343 unsigned num;
3344 int c;
3345 const char *p;
3346 char *(*match)(const char *, const char *);
3347
3348 jp = curjob;
3349 p = name;
3350 if (!p)
3351 goto currentjob;
3352
3353 if (*p != '%')
3354 goto err;
3355
3356 c = *++p;
3357 if (!c)
3358 goto currentjob;
3359
3360 if (!p[1]) {
3361 if (c == '+' || c == '%') {
3362 currentjob:
3363 err_msg = "No current job";
3364 goto check;
3365 }
3366 if (c == '-') {
3367 if (jp)
3368 jp = jp->prev_job;
3369 err_msg = "No previous job";
3370 check:
3371 if (!jp)
3372 goto err;
3373 goto gotit;
3374 }
3375 }
3376
3377 if (is_number(p)) {
3378 num = atoi(p);
3379 if (num < njobs) {
3380 jp = jobtab + num - 1;
3381 if (jp->used)
3382 goto gotit;
3383 goto err;
3384 }
3385 }
3386
3387 match = prefix;
3388 if (*p == '?') {
3389 match = strstr;
3390 p++;
3391 }
3392
3393 found = 0;
3394 while (1) {
3395 if (!jp)
3396 goto err;
3397 if (match(jp->ps[0].cmd, p)) {
3398 if (found)
3399 goto err;
3400 found = jp;
3401 err_msg = "%s: ambiguous";
3402 }
3403 jp = jp->prev_job;
3404 }
3405
3406 gotit:
3407#if JOBS
3408 err_msg = "job %s not created under job control";
3409 if (getctl && jp->jobctl == 0)
3410 goto err;
3411#endif
3412 return jp;
3413 err:
3414 ash_msg_and_raise_error(err_msg, name);
3415}
3416
3417/*
3418 * Mark a job structure as unused.
3419 */
3420static void
3421freejob(struct job *jp)
3422{
3423 struct procstat *ps;
3424 int i;
3425
3426 INT_OFF;
3427 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3428 if (ps->cmd != nullstr)
3429 free(ps->cmd);
3430 }
3431 if (jp->ps != &jp->ps0)
3432 free(jp->ps);
3433 jp->used = 0;
3434 set_curjob(jp, CUR_DELETE);
3435 INT_ON;
3436}
3437
3438#if JOBS
3439static void
3440xtcsetpgrp(int fd, pid_t pgrp)
3441{
3442 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003443 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003444}
3445
3446/*
3447 * Turn job control on and off.
3448 *
3449 * Note: This code assumes that the third arg to ioctl is a character
3450 * pointer, which is true on Berkeley systems but not System V. Since
3451 * System V doesn't have job control yet, this isn't a problem now.
3452 *
3453 * Called with interrupts off.
3454 */
3455static void
3456setjobctl(int on)
3457{
3458 int fd;
3459 int pgrp;
3460
3461 if (on == jobctl || rootshell == 0)
3462 return;
3463 if (on) {
3464 int ofd;
3465 ofd = fd = open(_PATH_TTY, O_RDWR);
3466 if (fd < 0) {
3467 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3468 * That sometimes helps to acquire controlling tty.
3469 * Obviously, a workaround for bugs when someone
3470 * failed to provide a controlling tty to bash! :) */
3471 fd += 3;
3472 while (!isatty(fd) && --fd >= 0)
3473 ;
3474 }
3475 fd = fcntl(fd, F_DUPFD, 10);
3476 close(ofd);
3477 if (fd < 0)
3478 goto out;
3479 fcntl(fd, F_SETFD, FD_CLOEXEC);
3480 do { /* while we are in the background */
3481 pgrp = tcgetpgrp(fd);
3482 if (pgrp < 0) {
3483 out:
3484 ash_msg("can't access tty; job control turned off");
3485 mflag = on = 0;
3486 goto close;
3487 }
3488 if (pgrp == getpgrp())
3489 break;
3490 killpg(0, SIGTTIN);
3491 } while (1);
3492 initialpgrp = pgrp;
3493
3494 setsignal(SIGTSTP);
3495 setsignal(SIGTTOU);
3496 setsignal(SIGTTIN);
3497 pgrp = rootpid;
3498 setpgid(0, pgrp);
3499 xtcsetpgrp(fd, pgrp);
3500 } else {
3501 /* turning job control off */
3502 fd = ttyfd;
3503 pgrp = initialpgrp;
3504 xtcsetpgrp(fd, pgrp);
3505 setpgid(0, pgrp);
3506 setsignal(SIGTSTP);
3507 setsignal(SIGTTOU);
3508 setsignal(SIGTTIN);
3509 close:
3510 close(fd);
3511 fd = -1;
3512 }
3513 ttyfd = fd;
3514 jobctl = on;
3515}
3516
3517static int
3518killcmd(int argc, char **argv)
3519{
3520 int signo = -1;
3521 int list = 0;
3522 int i;
3523 pid_t pid;
3524 struct job *jp;
3525
3526 if (argc <= 1) {
3527 usage:
3528 ash_msg_and_raise_error(
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003529"usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003530"kill -l [exitstatus]"
3531 );
3532 }
3533
3534 if (**++argv == '-') {
3535 signo = get_signum(*argv + 1);
3536 if (signo < 0) {
3537 int c;
3538
3539 while ((c = nextopt("ls:")) != '\0') {
3540 switch (c) {
3541 default:
3542#if DEBUG
3543 abort();
3544#endif
3545 case 'l':
3546 list = 1;
3547 break;
3548 case 's':
3549 signo = get_signum(optionarg);
3550 if (signo < 0) {
3551 ash_msg_and_raise_error(
3552 "invalid signal number or name: %s",
3553 optionarg
3554 );
3555 }
3556 break;
3557 }
3558 }
3559 argv = argptr;
3560 } else
3561 argv++;
3562 }
3563
3564 if (!list && signo < 0)
3565 signo = SIGTERM;
3566
3567 if ((signo < 0 || !*argv) ^ list) {
3568 goto usage;
3569 }
3570
3571 if (list) {
3572 const char *name;
3573
3574 if (!*argv) {
3575 for (i = 1; i < NSIG; i++) {
3576 name = get_signame(i);
Denis Vlasenkod7c81962007-04-11 20:43:31 +00003577 if (!isdigit(*name))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003578 out1fmt(snlfmt, name);
3579 }
3580 return 0;
3581 }
3582 name = get_signame(signo);
3583 if (!isdigit(*name))
3584 ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
3585 out1fmt(snlfmt, name);
3586 return 0;
3587 }
3588
3589 i = 0;
3590 do {
3591 if (**argv == '%') {
3592 jp = getjob(*argv, 0);
3593 pid = -jp->ps[0].pid;
3594 } else {
3595 pid = **argv == '-' ?
3596 -number(*argv + 1) : number(*argv);
3597 }
3598 if (kill(pid, signo) != 0) {
3599 ash_msg("(%d) - %m", pid);
3600 i = 1;
3601 }
3602 } while (*++argv);
3603
3604 return i;
3605}
3606
3607static void
3608showpipe(struct job *jp, FILE *out)
3609{
3610 struct procstat *sp;
3611 struct procstat *spend;
3612
3613 spend = jp->ps + jp->nprocs;
3614 for (sp = jp->ps + 1; sp < spend; sp++)
3615 fprintf(out, " | %s", sp->cmd);
3616 outcslow('\n', out);
3617 flush_stdout_stderr();
3618}
3619
3620
3621static int
3622restartjob(struct job *jp, int mode)
3623{
3624 struct procstat *ps;
3625 int i;
3626 int status;
3627 pid_t pgid;
3628
3629 INT_OFF;
3630 if (jp->state == JOBDONE)
3631 goto out;
3632 jp->state = JOBRUNNING;
3633 pgid = jp->ps->pid;
3634 if (mode == FORK_FG)
3635 xtcsetpgrp(ttyfd, pgid);
3636 killpg(pgid, SIGCONT);
3637 ps = jp->ps;
3638 i = jp->nprocs;
3639 do {
3640 if (WIFSTOPPED(ps->status)) {
3641 ps->status = -1;
3642 }
3643 } while (ps++, --i);
3644 out:
3645 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3646 INT_ON;
3647 return status;
3648}
3649
3650static int
3651fg_bgcmd(int argc, char **argv)
3652{
3653 struct job *jp;
3654 FILE *out;
3655 int mode;
3656 int retval;
3657
3658 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3659 nextopt(nullstr);
3660 argv = argptr;
3661 out = stdout;
3662 do {
3663 jp = getjob(*argv, 1);
3664 if (mode == FORK_BG) {
3665 set_curjob(jp, CUR_RUNNING);
3666 fprintf(out, "[%d] ", jobno(jp));
3667 }
3668 outstr(jp->ps->cmd, out);
3669 showpipe(jp, out);
3670 retval = restartjob(jp, mode);
3671 } while (*argv && *++argv);
3672 return retval;
3673}
3674#endif
3675
3676static int
3677sprint_status(char *s, int status, int sigonly)
3678{
3679 int col;
3680 int st;
3681
3682 col = 0;
3683 if (!WIFEXITED(status)) {
3684#if JOBS
3685 if (WIFSTOPPED(status))
3686 st = WSTOPSIG(status);
3687 else
3688#endif
3689 st = WTERMSIG(status);
3690 if (sigonly) {
3691 if (st == SIGINT || st == SIGPIPE)
3692 goto out;
3693#if JOBS
3694 if (WIFSTOPPED(status))
3695 goto out;
3696#endif
3697 }
3698 st &= 0x7f;
3699 col = fmtstr(s, 32, strsignal(st));
3700 if (WCOREDUMP(status)) {
3701 col += fmtstr(s + col, 16, " (core dumped)");
3702 }
3703 } else if (!sigonly) {
3704 st = WEXITSTATUS(status);
3705 if (st)
3706 col = fmtstr(s, 16, "Done(%d)", st);
3707 else
3708 col = fmtstr(s, 16, "Done");
3709 }
3710 out:
3711 return col;
3712}
3713
3714/*
3715 * Do a wait system call. If job control is compiled in, we accept
3716 * stopped processes. If block is zero, we return a value of zero
3717 * rather than blocking.
3718 *
3719 * System V doesn't have a non-blocking wait system call. It does
3720 * have a SIGCLD signal that is sent to a process when one of it's
3721 * children dies. The obvious way to use SIGCLD would be to install
3722 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3723 * was received, and have waitproc bump another counter when it got
3724 * the status of a process. Waitproc would then know that a wait
3725 * system call would not block if the two counters were different.
3726 * This approach doesn't work because if a process has children that
3727 * have not been waited for, System V will send it a SIGCLD when it
3728 * installs a signal handler for SIGCLD. What this means is that when
3729 * a child exits, the shell will be sent SIGCLD signals continuously
3730 * until is runs out of stack space, unless it does a wait call before
3731 * restoring the signal handler. The code below takes advantage of
3732 * this (mis)feature by installing a signal handler for SIGCLD and
3733 * then checking to see whether it was called. If there are any
3734 * children to be waited for, it will be.
3735 *
3736 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3737 * waits at all. In this case, the user will not be informed when
3738 * a background process until the next time she runs a real program
3739 * (as opposed to running a builtin command or just typing return),
3740 * and the jobs command may give out of date information.
3741 */
3742static int
3743waitproc(int block, int *status)
3744{
3745 int flags = 0;
3746
3747#if JOBS
3748 if (jobctl)
3749 flags |= WUNTRACED;
3750#endif
3751 if (block == 0)
3752 flags |= WNOHANG;
3753 return wait3(status, flags, (struct rusage *)NULL);
3754}
3755
3756/*
3757 * Wait for a process to terminate.
3758 */
3759static int
3760dowait(int block, struct job *job)
3761{
3762 int pid;
3763 int status;
3764 struct job *jp;
3765 struct job *thisjob;
3766 int state;
3767
3768 TRACE(("dowait(%d) called\n", block));
3769 pid = waitproc(block, &status);
3770 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3771 if (pid <= 0)
3772 return pid;
3773 INT_OFF;
3774 thisjob = NULL;
3775 for (jp = curjob; jp; jp = jp->prev_job) {
3776 struct procstat *sp;
3777 struct procstat *spend;
3778 if (jp->state == JOBDONE)
3779 continue;
3780 state = JOBDONE;
3781 spend = jp->ps + jp->nprocs;
3782 sp = jp->ps;
3783 do {
3784 if (sp->pid == pid) {
3785 TRACE(("Job %d: changing status of proc %d "
3786 "from 0x%x to 0x%x\n",
3787 jobno(jp), pid, sp->status, status));
3788 sp->status = status;
3789 thisjob = jp;
3790 }
3791 if (sp->status == -1)
3792 state = JOBRUNNING;
3793#if JOBS
3794 if (state == JOBRUNNING)
3795 continue;
3796 if (WIFSTOPPED(sp->status)) {
3797 jp->stopstatus = sp->status;
3798 state = JOBSTOPPED;
3799 }
3800#endif
3801 } while (++sp < spend);
3802 if (thisjob)
3803 goto gotjob;
3804 }
3805#if JOBS
3806 if (!WIFSTOPPED(status))
3807#endif
3808
3809 jobless--;
3810 goto out;
3811
3812 gotjob:
3813 if (state != JOBRUNNING) {
3814 thisjob->changed = 1;
3815
3816 if (thisjob->state != state) {
3817 TRACE(("Job %d: changing state from %d to %d\n",
3818 jobno(thisjob), thisjob->state, state));
3819 thisjob->state = state;
3820#if JOBS
3821 if (state == JOBSTOPPED) {
3822 set_curjob(thisjob, CUR_STOPPED);
3823 }
3824#endif
3825 }
3826 }
3827
3828 out:
3829 INT_ON;
3830
3831 if (thisjob && thisjob == job) {
3832 char s[48 + 1];
3833 int len;
3834
3835 len = sprint_status(s, status, 1);
3836 if (len) {
3837 s[len] = '\n';
3838 s[len + 1] = 0;
3839 out2str(s);
3840 }
3841 }
3842 return pid;
3843}
3844
3845#if JOBS
3846static void
3847showjob(FILE *out, struct job *jp, int mode)
3848{
3849 struct procstat *ps;
3850 struct procstat *psend;
3851 int col;
3852 int indent;
3853 char s[80];
3854
3855 ps = jp->ps;
3856
3857 if (mode & SHOW_PGID) {
3858 /* just output process (group) id of pipeline */
3859 fprintf(out, "%d\n", ps->pid);
3860 return;
3861 }
3862
3863 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3864 indent = col;
3865
3866 if (jp == curjob)
3867 s[col - 2] = '+';
3868 else if (curjob && jp == curjob->prev_job)
3869 s[col - 2] = '-';
3870
3871 if (mode & SHOW_PID)
3872 col += fmtstr(s + col, 16, "%d ", ps->pid);
3873
3874 psend = ps + jp->nprocs;
3875
3876 if (jp->state == JOBRUNNING) {
3877 strcpy(s + col, "Running");
3878 col += sizeof("Running") - 1;
3879 } else {
3880 int status = psend[-1].status;
3881 if (jp->state == JOBSTOPPED)
3882 status = jp->stopstatus;
3883 col += sprint_status(s + col, status, 0);
3884 }
3885
3886 goto start;
3887
3888 do {
3889 /* for each process */
3890 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
3891 start:
3892 fprintf(out, "%s%*c%s",
3893 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3894 );
3895 if (!(mode & SHOW_PID)) {
3896 showpipe(jp, out);
3897 break;
3898 }
3899 if (++ps == psend) {
3900 outcslow('\n', out);
3901 break;
3902 }
3903 } while (1);
3904
3905 jp->changed = 0;
3906
3907 if (jp->state == JOBDONE) {
3908 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3909 freejob(jp);
3910 }
3911}
3912
3913static int
3914jobscmd(int argc, char **argv)
3915{
3916 int mode, m;
3917 FILE *out;
3918
3919 mode = 0;
3920 while ((m = nextopt("lp"))) {
3921 if (m == 'l')
3922 mode = SHOW_PID;
3923 else
3924 mode = SHOW_PGID;
3925 }
3926
3927 out = stdout;
3928 argv = argptr;
3929 if (*argv) {
3930 do
3931 showjob(out, getjob(*argv,0), mode);
3932 while (*++argv);
3933 } else
3934 showjobs(out, mode);
3935
3936 return 0;
3937}
3938
3939/*
3940 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3941 * statuses have changed since the last call to showjobs.
3942 */
3943static void
3944showjobs(FILE *out, int mode)
3945{
3946 struct job *jp;
3947
3948 TRACE(("showjobs(%x) called\n", mode));
3949
3950 /* If not even one one job changed, there is nothing to do */
3951 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3952 continue;
3953
3954 for (jp = curjob; jp; jp = jp->prev_job) {
3955 if (!(mode & SHOW_CHANGED) || jp->changed)
3956 showjob(out, jp, mode);
3957 }
3958}
3959#endif /* JOBS */
3960
3961static int
3962getstatus(struct job *job)
3963{
3964 int status;
3965 int retval;
3966
3967 status = job->ps[job->nprocs - 1].status;
3968 retval = WEXITSTATUS(status);
3969 if (!WIFEXITED(status)) {
3970#if JOBS
3971 retval = WSTOPSIG(status);
3972 if (!WIFSTOPPED(status))
3973#endif
3974 {
3975 /* XXX: limits number of signals */
3976 retval = WTERMSIG(status);
3977#if JOBS
3978 if (retval == SIGINT)
3979 job->sigint = 1;
3980#endif
3981 }
3982 retval += 128;
3983 }
3984 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3985 jobno(job), job->nprocs, status, retval));
3986 return retval;
3987}
3988
3989static int
3990waitcmd(int argc, char **argv)
3991{
3992 struct job *job;
3993 int retval;
3994 struct job *jp;
3995
3996 EXSIGON;
3997
3998 nextopt(nullstr);
3999 retval = 0;
4000
4001 argv = argptr;
4002 if (!*argv) {
4003 /* wait for all jobs */
4004 for (;;) {
4005 jp = curjob;
4006 while (1) {
4007 if (!jp) {
4008 /* no running procs */
4009 goto out;
4010 }
4011 if (jp->state == JOBRUNNING)
4012 break;
4013 jp->waited = 1;
4014 jp = jp->prev_job;
4015 }
4016 dowait(DOWAIT_BLOCK, 0);
4017 }
4018 }
4019
4020 retval = 127;
4021 do {
4022 if (**argv != '%') {
4023 pid_t pid = number(*argv);
4024 job = curjob;
4025 goto start;
4026 do {
4027 if (job->ps[job->nprocs - 1].pid == pid)
4028 break;
4029 job = job->prev_job;
4030 start:
4031 if (!job)
4032 goto repeat;
4033 } while (1);
4034 } else
4035 job = getjob(*argv, 0);
4036 /* loop until process terminated or stopped */
4037 while (job->state == JOBRUNNING)
4038 dowait(DOWAIT_BLOCK, 0);
4039 job->waited = 1;
4040 retval = getstatus(job);
4041 repeat:
4042 ;
4043 } while (*++argv);
4044
4045 out:
4046 return retval;
4047}
4048
4049static struct job *
4050growjobtab(void)
4051{
4052 size_t len;
4053 ptrdiff_t offset;
4054 struct job *jp, *jq;
4055
4056 len = njobs * sizeof(*jp);
4057 jq = jobtab;
4058 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4059
4060 offset = (char *)jp - (char *)jq;
4061 if (offset) {
4062 /* Relocate pointers */
4063 size_t l = len;
4064
4065 jq = (struct job *)((char *)jq + l);
4066 while (l) {
4067 l -= sizeof(*jp);
4068 jq--;
4069#define joff(p) ((struct job *)((char *)(p) + l))
4070#define jmove(p) (p) = (void *)((char *)(p) + offset)
4071 if (joff(jp)->ps == &jq->ps0)
4072 jmove(joff(jp)->ps);
4073 if (joff(jp)->prev_job)
4074 jmove(joff(jp)->prev_job);
4075 }
4076 if (curjob)
4077 jmove(curjob);
4078#undef joff
4079#undef jmove
4080 }
4081
4082 njobs += 4;
4083 jobtab = jp;
4084 jp = (struct job *)((char *)jp + len);
4085 jq = jp + 3;
4086 do {
4087 jq->used = 0;
4088 } while (--jq >= jp);
4089 return jp;
4090}
4091
4092/*
4093 * Return a new job structure.
4094 * Called with interrupts off.
4095 */
4096static struct job *
4097makejob(union node *node, int nprocs)
4098{
4099 int i;
4100 struct job *jp;
4101
4102 for (i = njobs, jp = jobtab; ; jp++) {
4103 if (--i < 0) {
4104 jp = growjobtab();
4105 break;
4106 }
4107 if (jp->used == 0)
4108 break;
4109 if (jp->state != JOBDONE || !jp->waited)
4110 continue;
4111#if JOBS
4112 if (jobctl)
4113 continue;
4114#endif
4115 freejob(jp);
4116 break;
4117 }
4118 memset(jp, 0, sizeof(*jp));
4119#if JOBS
4120 if (jobctl)
4121 jp->jobctl = 1;
4122#endif
4123 jp->prev_job = curjob;
4124 curjob = jp;
4125 jp->used = 1;
4126 jp->ps = &jp->ps0;
4127 if (nprocs > 1) {
4128 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4129 }
4130 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4131 jobno(jp)));
4132 return jp;
4133}
4134
4135#if JOBS
4136/*
4137 * Return a string identifying a command (to be printed by the
4138 * jobs command).
4139 */
4140static char *cmdnextc;
4141
4142static void
4143cmdputs(const char *s)
4144{
4145 const char *p, *str;
4146 char c, cc[2] = " ";
4147 char *nextc;
4148 int subtype = 0;
4149 int quoted = 0;
4150 static const char vstype[VSTYPE + 1][4] = {
4151 "", "}", "-", "+", "?", "=",
4152 "%", "%%", "#", "##"
4153 };
4154
4155 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4156 p = s;
4157 while ((c = *p++) != 0) {
4158 str = 0;
4159 switch (c) {
4160 case CTLESC:
4161 c = *p++;
4162 break;
4163 case CTLVAR:
4164 subtype = *p++;
4165 if ((subtype & VSTYPE) == VSLENGTH)
4166 str = "${#";
4167 else
4168 str = "${";
4169 if (!(subtype & VSQUOTE) == !(quoted & 1))
4170 goto dostr;
4171 quoted ^= 1;
4172 c = '"';
4173 break;
4174 case CTLENDVAR:
4175 str = "\"}" + !(quoted & 1);
4176 quoted >>= 1;
4177 subtype = 0;
4178 goto dostr;
4179 case CTLBACKQ:
4180 str = "$(...)";
4181 goto dostr;
4182 case CTLBACKQ+CTLQUOTE:
4183 str = "\"$(...)\"";
4184 goto dostr;
4185#if ENABLE_ASH_MATH_SUPPORT
4186 case CTLARI:
4187 str = "$((";
4188 goto dostr;
4189 case CTLENDARI:
4190 str = "))";
4191 goto dostr;
4192#endif
4193 case CTLQUOTEMARK:
4194 quoted ^= 1;
4195 c = '"';
4196 break;
4197 case '=':
4198 if (subtype == 0)
4199 break;
4200 if ((subtype & VSTYPE) != VSNORMAL)
4201 quoted <<= 1;
4202 str = vstype[subtype & VSTYPE];
4203 if (subtype & VSNUL)
4204 c = ':';
4205 else
4206 goto checkstr;
4207 break;
4208 case '\'':
4209 case '\\':
4210 case '"':
4211 case '$':
4212 /* These can only happen inside quotes */
4213 cc[0] = c;
4214 str = cc;
4215 c = '\\';
4216 break;
4217 default:
4218 break;
4219 }
4220 USTPUTC(c, nextc);
4221 checkstr:
4222 if (!str)
4223 continue;
4224 dostr:
4225 while ((c = *str++)) {
4226 USTPUTC(c, nextc);
4227 }
4228 }
4229 if (quoted & 1) {
4230 USTPUTC('"', nextc);
4231 }
4232 *nextc = 0;
4233 cmdnextc = nextc;
4234}
4235
4236/* cmdtxt() and cmdlist() call each other */
4237static void cmdtxt(union node *n);
4238
4239static void
4240cmdlist(union node *np, int sep)
4241{
4242 for (; np; np = np->narg.next) {
4243 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004244 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004245 cmdtxt(np);
4246 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004247 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004248 }
4249}
4250
4251static void
4252cmdtxt(union node *n)
4253{
4254 union node *np;
4255 struct nodelist *lp;
4256 const char *p;
4257 char s[2];
4258
4259 if (!n)
4260 return;
4261 switch (n->type) {
4262 default:
4263#if DEBUG
4264 abort();
4265#endif
4266 case NPIPE:
4267 lp = n->npipe.cmdlist;
4268 for (;;) {
4269 cmdtxt(lp->n);
4270 lp = lp->next;
4271 if (!lp)
4272 break;
4273 cmdputs(" | ");
4274 }
4275 break;
4276 case NSEMI:
4277 p = "; ";
4278 goto binop;
4279 case NAND:
4280 p = " && ";
4281 goto binop;
4282 case NOR:
4283 p = " || ";
4284 binop:
4285 cmdtxt(n->nbinary.ch1);
4286 cmdputs(p);
4287 n = n->nbinary.ch2;
4288 goto donode;
4289 case NREDIR:
4290 case NBACKGND:
4291 n = n->nredir.n;
4292 goto donode;
4293 case NNOT:
4294 cmdputs("!");
4295 n = n->nnot.com;
4296 donode:
4297 cmdtxt(n);
4298 break;
4299 case NIF:
4300 cmdputs("if ");
4301 cmdtxt(n->nif.test);
4302 cmdputs("; then ");
4303 n = n->nif.ifpart;
4304 if (n->nif.elsepart) {
4305 cmdtxt(n);
4306 cmdputs("; else ");
4307 n = n->nif.elsepart;
4308 }
4309 p = "; fi";
4310 goto dotail;
4311 case NSUBSHELL:
4312 cmdputs("(");
4313 n = n->nredir.n;
4314 p = ")";
4315 goto dotail;
4316 case NWHILE:
4317 p = "while ";
4318 goto until;
4319 case NUNTIL:
4320 p = "until ";
4321 until:
4322 cmdputs(p);
4323 cmdtxt(n->nbinary.ch1);
4324 n = n->nbinary.ch2;
4325 p = "; done";
4326 dodo:
4327 cmdputs("; do ");
4328 dotail:
4329 cmdtxt(n);
4330 goto dotail2;
4331 case NFOR:
4332 cmdputs("for ");
4333 cmdputs(n->nfor.var);
4334 cmdputs(" in ");
4335 cmdlist(n->nfor.args, 1);
4336 n = n->nfor.body;
4337 p = "; done";
4338 goto dodo;
4339 case NDEFUN:
4340 cmdputs(n->narg.text);
4341 p = "() { ... }";
4342 goto dotail2;
4343 case NCMD:
4344 cmdlist(n->ncmd.args, 1);
4345 cmdlist(n->ncmd.redirect, 0);
4346 break;
4347 case NARG:
4348 p = n->narg.text;
4349 dotail2:
4350 cmdputs(p);
4351 break;
4352 case NHERE:
4353 case NXHERE:
4354 p = "<<...";
4355 goto dotail2;
4356 case NCASE:
4357 cmdputs("case ");
4358 cmdputs(n->ncase.expr->narg.text);
4359 cmdputs(" in ");
4360 for (np = n->ncase.cases; np; np = np->nclist.next) {
4361 cmdtxt(np->nclist.pattern);
4362 cmdputs(") ");
4363 cmdtxt(np->nclist.body);
4364 cmdputs(";; ");
4365 }
4366 p = "esac";
4367 goto dotail2;
4368 case NTO:
4369 p = ">";
4370 goto redir;
4371 case NCLOBBER:
4372 p = ">|";
4373 goto redir;
4374 case NAPPEND:
4375 p = ">>";
4376 goto redir;
4377 case NTOFD:
4378 p = ">&";
4379 goto redir;
4380 case NFROM:
4381 p = "<";
4382 goto redir;
4383 case NFROMFD:
4384 p = "<&";
4385 goto redir;
4386 case NFROMTO:
4387 p = "<>";
4388 redir:
4389 s[0] = n->nfile.fd + '0';
4390 s[1] = '\0';
4391 cmdputs(s);
4392 cmdputs(p);
4393 if (n->type == NTOFD || n->type == NFROMFD) {
4394 s[0] = n->ndup.dupfd + '0';
4395 p = s;
4396 goto dotail2;
4397 }
4398 n = n->nfile.fname;
4399 goto donode;
4400 }
4401}
4402
4403static char *
4404commandtext(union node *n)
4405{
4406 char *name;
4407
4408 STARTSTACKSTR(cmdnextc);
4409 cmdtxt(n);
4410 name = stackblock();
4411 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4412 name, cmdnextc, cmdnextc));
4413 return ckstrdup(name);
4414}
4415#endif /* JOBS */
4416
4417/*
4418 * Fork off a subshell. If we are doing job control, give the subshell its
4419 * own process group. Jp is a job structure that the job is to be added to.
4420 * N is the command that will be evaluated by the child. Both jp and n may
4421 * be NULL. The mode parameter can be one of the following:
4422 * FORK_FG - Fork off a foreground process.
4423 * FORK_BG - Fork off a background process.
4424 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4425 * process group even if job control is on.
4426 *
4427 * When job control is turned off, background processes have their standard
4428 * input redirected to /dev/null (except for the second and later processes
4429 * in a pipeline).
4430 *
4431 * Called with interrupts off.
4432 */
4433/*
4434 * Clear traps on a fork.
4435 */
4436static void
4437clear_traps(void)
4438{
4439 char **tp;
4440
4441 for (tp = trap; tp < &trap[NSIG]; tp++) {
4442 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4443 INT_OFF;
4444 free(*tp);
4445 *tp = NULL;
4446 if (tp != &trap[0])
4447 setsignal(tp - trap);
4448 INT_ON;
4449 }
4450 }
4451}
4452/* lives far away from here, needed for forkchild */
4453static void closescript(void);
4454static void
4455forkchild(struct job *jp, union node *n, int mode)
4456{
4457 int oldlvl;
4458
4459 TRACE(("Child shell %d\n", getpid()));
4460 oldlvl = shlvl;
4461 shlvl++;
4462
4463 closescript();
4464 clear_traps();
4465#if JOBS
4466 /* do job control only in root shell */
4467 jobctl = 0;
4468 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4469 pid_t pgrp;
4470
4471 if (jp->nprocs == 0)
4472 pgrp = getpid();
4473 else
4474 pgrp = jp->ps[0].pid;
4475 /* This can fail because we are doing it in the parent also */
4476 (void)setpgid(0, pgrp);
4477 if (mode == FORK_FG)
4478 xtcsetpgrp(ttyfd, pgrp);
4479 setsignal(SIGTSTP);
4480 setsignal(SIGTTOU);
4481 } else
4482#endif
4483 if (mode == FORK_BG) {
4484 ignoresig(SIGINT);
4485 ignoresig(SIGQUIT);
4486 if (jp->nprocs == 0) {
4487 close(0);
4488 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004489 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004490 }
4491 }
4492 if (!oldlvl && iflag) {
4493 setsignal(SIGINT);
4494 setsignal(SIGQUIT);
4495 setsignal(SIGTERM);
4496 }
4497 for (jp = curjob; jp; jp = jp->prev_job)
4498 freejob(jp);
4499 jobless = 0;
4500}
4501
4502static void
4503forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4504{
4505 TRACE(("In parent shell: child = %d\n", pid));
4506 if (!jp) {
4507 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4508 jobless++;
4509 return;
4510 }
4511#if JOBS
4512 if (mode != FORK_NOJOB && jp->jobctl) {
4513 int pgrp;
4514
4515 if (jp->nprocs == 0)
4516 pgrp = pid;
4517 else
4518 pgrp = jp->ps[0].pid;
4519 /* This can fail because we are doing it in the child also */
4520 setpgid(pid, pgrp);
4521 }
4522#endif
4523 if (mode == FORK_BG) {
4524 backgndpid = pid; /* set $! */
4525 set_curjob(jp, CUR_RUNNING);
4526 }
4527 if (jp) {
4528 struct procstat *ps = &jp->ps[jp->nprocs++];
4529 ps->pid = pid;
4530 ps->status = -1;
4531 ps->cmd = nullstr;
4532#if JOBS
4533 if (jobctl && n)
4534 ps->cmd = commandtext(n);
4535#endif
4536 }
4537}
4538
4539static int
4540forkshell(struct job *jp, union node *n, int mode)
4541{
4542 int pid;
4543
4544 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4545 pid = fork();
4546 if (pid < 0) {
4547 TRACE(("Fork failed, errno=%d", errno));
4548 if (jp)
4549 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004550 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004551 }
4552 if (pid == 0)
4553 forkchild(jp, n, mode);
4554 else
4555 forkparent(jp, n, mode, pid);
4556 return pid;
4557}
4558
4559/*
4560 * Wait for job to finish.
4561 *
4562 * Under job control we have the problem that while a child process is
4563 * running interrupts generated by the user are sent to the child but not
4564 * to the shell. This means that an infinite loop started by an inter-
4565 * active user may be hard to kill. With job control turned off, an
4566 * interactive user may place an interactive program inside a loop. If
4567 * the interactive program catches interrupts, the user doesn't want
4568 * these interrupts to also abort the loop. The approach we take here
4569 * is to have the shell ignore interrupt signals while waiting for a
4570 * foreground process to terminate, and then send itself an interrupt
4571 * signal if the child process was terminated by an interrupt signal.
4572 * Unfortunately, some programs want to do a bit of cleanup and then
4573 * exit on interrupt; unless these processes terminate themselves by
4574 * sending a signal to themselves (instead of calling exit) they will
4575 * confuse this approach.
4576 *
4577 * Called with interrupts off.
4578 */
4579static int
4580waitforjob(struct job *jp)
4581{
4582 int st;
4583
4584 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4585 while (jp->state == JOBRUNNING) {
4586 dowait(DOWAIT_BLOCK, jp);
4587 }
4588 st = getstatus(jp);
4589#if JOBS
4590 if (jp->jobctl) {
4591 xtcsetpgrp(ttyfd, rootpid);
4592 /*
4593 * This is truly gross.
4594 * If we're doing job control, then we did a TIOCSPGRP which
4595 * caused us (the shell) to no longer be in the controlling
4596 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4597 * intuit from the subprocess exit status whether a SIGINT
4598 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4599 */
4600 if (jp->sigint)
4601 raise(SIGINT);
4602 }
4603 if (jp->state == JOBDONE)
4604#endif
4605 freejob(jp);
4606 return st;
4607}
4608
4609/*
4610 * return 1 if there are stopped jobs, otherwise 0
4611 */
4612static int
4613stoppedjobs(void)
4614{
4615 struct job *jp;
4616 int retval;
4617
4618 retval = 0;
4619 if (job_warning)
4620 goto out;
4621 jp = curjob;
4622 if (jp && jp->state == JOBSTOPPED) {
4623 out2str("You have stopped jobs.\n");
4624 job_warning = 2;
4625 retval++;
4626 }
4627 out:
4628 return retval;
4629}
4630
4631
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004632/* ============ redir.c
4633 *
4634 * Code for dealing with input/output redirection.
4635 */
4636
4637#define EMPTY -2 /* marks an unused slot in redirtab */
4638#ifndef PIPE_BUF
4639# define PIPESIZE 4096 /* amount of buffering in a pipe */
4640#else
4641# define PIPESIZE PIPE_BUF
4642#endif
4643
4644/*
4645 * Open a file in noclobber mode.
4646 * The code was copied from bash.
4647 */
4648static int
4649noclobberopen(const char *fname)
4650{
4651 int r, fd;
4652 struct stat finfo, finfo2;
4653
4654 /*
4655 * If the file exists and is a regular file, return an error
4656 * immediately.
4657 */
4658 r = stat(fname, &finfo);
4659 if (r == 0 && S_ISREG(finfo.st_mode)) {
4660 errno = EEXIST;
4661 return -1;
4662 }
4663
4664 /*
4665 * If the file was not present (r != 0), make sure we open it
4666 * exclusively so that if it is created before we open it, our open
4667 * will fail. Make sure that we do not truncate an existing file.
4668 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4669 * file was not a regular file, we leave O_EXCL off.
4670 */
4671 if (r != 0)
4672 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4673 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4674
4675 /* If the open failed, return the file descriptor right away. */
4676 if (fd < 0)
4677 return fd;
4678
4679 /*
4680 * OK, the open succeeded, but the file may have been changed from a
4681 * non-regular file to a regular file between the stat and the open.
4682 * We are assuming that the O_EXCL open handles the case where FILENAME
4683 * did not exist and is symlinked to an existing file between the stat
4684 * and open.
4685 */
4686
4687 /*
4688 * If we can open it and fstat the file descriptor, and neither check
4689 * revealed that it was a regular file, and the file has not been
4690 * replaced, return the file descriptor.
4691 */
4692 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4693 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4694 return fd;
4695
4696 /* The file has been replaced. badness. */
4697 close(fd);
4698 errno = EEXIST;
4699 return -1;
4700}
4701
4702/*
4703 * Handle here documents. Normally we fork off a process to write the
4704 * data to a pipe. If the document is short, we can stuff the data in
4705 * the pipe without forking.
4706 */
4707/* openhere needs this forward reference */
4708static void expandhere(union node *arg, int fd);
4709static int
4710openhere(union node *redir)
4711{
4712 int pip[2];
4713 size_t len = 0;
4714
4715 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004716 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004717 if (redir->type == NHERE) {
4718 len = strlen(redir->nhere.doc->narg.text);
4719 if (len <= PIPESIZE) {
4720 full_write(pip[1], redir->nhere.doc->narg.text, len);
4721 goto out;
4722 }
4723 }
4724 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4725 close(pip[0]);
4726 signal(SIGINT, SIG_IGN);
4727 signal(SIGQUIT, SIG_IGN);
4728 signal(SIGHUP, SIG_IGN);
4729#ifdef SIGTSTP
4730 signal(SIGTSTP, SIG_IGN);
4731#endif
4732 signal(SIGPIPE, SIG_DFL);
4733 if (redir->type == NHERE)
4734 full_write(pip[1], redir->nhere.doc->narg.text, len);
4735 else
4736 expandhere(redir->nhere.doc, pip[1]);
4737 _exit(0);
4738 }
4739 out:
4740 close(pip[1]);
4741 return pip[0];
4742}
4743
4744static int
4745openredirect(union node *redir)
4746{
4747 char *fname;
4748 int f;
4749
4750 switch (redir->nfile.type) {
4751 case NFROM:
4752 fname = redir->nfile.expfname;
4753 f = open(fname, O_RDONLY);
4754 if (f < 0)
4755 goto eopen;
4756 break;
4757 case NFROMTO:
4758 fname = redir->nfile.expfname;
4759 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4760 if (f < 0)
4761 goto ecreate;
4762 break;
4763 case NTO:
4764 /* Take care of noclobber mode. */
4765 if (Cflag) {
4766 fname = redir->nfile.expfname;
4767 f = noclobberopen(fname);
4768 if (f < 0)
4769 goto ecreate;
4770 break;
4771 }
4772 /* FALLTHROUGH */
4773 case NCLOBBER:
4774 fname = redir->nfile.expfname;
4775 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4776 if (f < 0)
4777 goto ecreate;
4778 break;
4779 case NAPPEND:
4780 fname = redir->nfile.expfname;
4781 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4782 if (f < 0)
4783 goto ecreate;
4784 break;
4785 default:
4786#if DEBUG
4787 abort();
4788#endif
4789 /* Fall through to eliminate warning. */
4790 case NTOFD:
4791 case NFROMFD:
4792 f = -1;
4793 break;
4794 case NHERE:
4795 case NXHERE:
4796 f = openhere(redir);
4797 break;
4798 }
4799
4800 return f;
4801 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004802 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004803 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004804 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004805}
4806
4807/*
4808 * Copy a file descriptor to be >= to. Returns -1
4809 * if the source file descriptor is closed, EMPTY if there are no unused
4810 * file descriptors left.
4811 */
4812static int
4813copyfd(int from, int to)
4814{
4815 int newfd;
4816
4817 newfd = fcntl(from, F_DUPFD, to);
4818 if (newfd < 0) {
4819 if (errno == EMFILE)
4820 return EMPTY;
4821 ash_msg_and_raise_error("%d: %m", from);
4822 }
4823 return newfd;
4824}
4825
4826static void
4827dupredirect(union node *redir, int f)
4828{
4829 int fd = redir->nfile.fd;
4830
4831 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4832 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4833 copyfd(redir->ndup.dupfd, fd);
4834 }
4835 return;
4836 }
4837
4838 if (f != fd) {
4839 copyfd(f, fd);
4840 close(f);
4841 }
4842}
4843
4844/*
4845 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4846 * old file descriptors are stashed away so that the redirection can be
4847 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4848 * standard output, and the standard error if it becomes a duplicate of
4849 * stdout, is saved in memory.
4850 */
4851/* flags passed to redirect */
4852#define REDIR_PUSH 01 /* save previous values of file descriptors */
4853#define REDIR_SAVEFD2 03 /* set preverrout */
4854static void
4855redirect(union node *redir, int flags)
4856{
4857 union node *n;
4858 struct redirtab *sv;
4859 int i;
4860 int fd;
4861 int newfd;
4862 int *p;
4863 nullredirs++;
4864 if (!redir) {
4865 return;
4866 }
4867 sv = NULL;
4868 INT_OFF;
4869 if (flags & REDIR_PUSH) {
4870 struct redirtab *q;
4871 q = ckmalloc(sizeof(struct redirtab));
4872 q->next = redirlist;
4873 redirlist = q;
4874 q->nullredirs = nullredirs - 1;
4875 for (i = 0; i < 10; i++)
4876 q->renamed[i] = EMPTY;
4877 nullredirs = 0;
4878 sv = q;
4879 }
4880 n = redir;
4881 do {
4882 fd = n->nfile.fd;
4883 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4884 && n->ndup.dupfd == fd)
4885 continue; /* redirect from/to same file descriptor */
4886
4887 newfd = openredirect(n);
4888 if (fd == newfd)
4889 continue;
4890 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4891 i = fcntl(fd, F_DUPFD, 10);
4892
4893 if (i == -1) {
4894 i = errno;
4895 if (i != EBADF) {
4896 close(newfd);
4897 errno = i;
4898 ash_msg_and_raise_error("%d: %m", fd);
4899 /* NOTREACHED */
4900 }
4901 } else {
4902 *p = i;
4903 close(fd);
4904 }
4905 } else {
4906 close(fd);
4907 }
4908 dupredirect(n, newfd);
4909 } while ((n = n->nfile.next));
4910 INT_ON;
4911 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4912 preverrout_fd = sv->renamed[2];
4913}
4914
4915/*
4916 * Undo the effects of the last redirection.
4917 */
4918static void
4919popredir(int drop)
4920{
4921 struct redirtab *rp;
4922 int i;
4923
4924 if (--nullredirs >= 0)
4925 return;
4926 INT_OFF;
4927 rp = redirlist;
4928 for (i = 0; i < 10; i++) {
4929 if (rp->renamed[i] != EMPTY) {
4930 if (!drop) {
4931 close(i);
4932 copyfd(rp->renamed[i], i);
4933 }
4934 close(rp->renamed[i]);
4935 }
4936 }
4937 redirlist = rp->next;
4938 nullredirs = rp->nullredirs;
4939 free(rp);
4940 INT_ON;
4941}
4942
4943/*
4944 * Undo all redirections. Called on error or interrupt.
4945 */
4946
4947/*
4948 * Discard all saved file descriptors.
4949 */
4950static void
4951clearredir(int drop)
4952{
4953 for (;;) {
4954 nullredirs = 0;
4955 if (!redirlist)
4956 break;
4957 popredir(drop);
4958 }
4959}
4960
4961static int
4962redirectsafe(union node *redir, int flags)
4963{
4964 int err;
4965 volatile int saveint;
4966 struct jmploc *volatile savehandler = exception_handler;
4967 struct jmploc jmploc;
4968
4969 SAVE_INT(saveint);
4970 err = setjmp(jmploc.loc) * 2;
4971 if (!err) {
4972 exception_handler = &jmploc;
4973 redirect(redir, flags);
4974 }
4975 exception_handler = savehandler;
4976 if (err && exception != EXERROR)
4977 longjmp(exception_handler->loc, 1);
4978 RESTORE_INT(saveint);
4979 return err;
4980}
4981
4982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004983/* ============ Routines to expand arguments to commands
4984 *
4985 * We have to deal with backquotes, shell variables, and file metacharacters.
4986 */
4987
4988/*
4989 * expandarg flags
4990 */
4991#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4992#define EXP_TILDE 0x2 /* do normal tilde expansion */
4993#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4994#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4995#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4996#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4997#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4998#define EXP_WORD 0x80 /* expand word in parameter expansion */
4999#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5000/*
5001 * _rmescape() flags
5002 */
5003#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5004#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5005#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5006#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5007#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5008
5009/*
5010 * Structure specifying which parts of the string should be searched
5011 * for IFS characters.
5012 */
5013struct ifsregion {
5014 struct ifsregion *next; /* next region in list */
5015 int begoff; /* offset of start of region */
5016 int endoff; /* offset of end of region */
5017 int nulonly; /* search for nul bytes only */
5018};
5019
5020struct arglist {
5021 struct strlist *list;
5022 struct strlist **lastp;
5023};
5024
5025/* output of current string */
5026static char *expdest;
5027/* list of back quote expressions */
5028static struct nodelist *argbackq;
5029/* first struct in list of ifs regions */
5030static struct ifsregion ifsfirst;
5031/* last struct in list */
5032static struct ifsregion *ifslastp;
5033/* holds expanded arg list */
5034static struct arglist exparg;
5035
5036/*
5037 * Our own itoa().
5038 */
5039static int
5040cvtnum(arith_t num)
5041{
5042 int len;
5043
5044 expdest = makestrspace(32, expdest);
5045#if ENABLE_ASH_MATH_SUPPORT_64
5046 len = fmtstr(expdest, 32, "%lld", (long long) num);
5047#else
5048 len = fmtstr(expdest, 32, "%ld", num);
5049#endif
5050 STADJUST(len, expdest);
5051 return len;
5052}
5053
5054static size_t
5055esclen(const char *start, const char *p)
5056{
5057 size_t esc = 0;
5058
5059 while (p > start && *--p == CTLESC) {
5060 esc++;
5061 }
5062 return esc;
5063}
5064
5065/*
5066 * Remove any CTLESC characters from a string.
5067 */
5068static char *
5069_rmescapes(char *str, int flag)
5070{
5071 char *p, *q, *r;
5072 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5073 unsigned inquotes;
5074 int notescaped;
5075 int globbing;
5076
5077 p = strpbrk(str, qchars);
5078 if (!p) {
5079 return str;
5080 }
5081 q = p;
5082 r = str;
5083 if (flag & RMESCAPE_ALLOC) {
5084 size_t len = p - str;
5085 size_t fulllen = len + strlen(p) + 1;
5086
5087 if (flag & RMESCAPE_GROW) {
5088 r = makestrspace(fulllen, expdest);
5089 } else if (flag & RMESCAPE_HEAP) {
5090 r = ckmalloc(fulllen);
5091 } else {
5092 r = stalloc(fulllen);
5093 }
5094 q = r;
5095 if (len > 0) {
5096 q = memcpy(q, str, len) + len;
5097 }
5098 }
5099 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5100 globbing = flag & RMESCAPE_GLOB;
5101 notescaped = globbing;
5102 while (*p) {
5103 if (*p == CTLQUOTEMARK) {
5104 inquotes = ~inquotes;
5105 p++;
5106 notescaped = globbing;
5107 continue;
5108 }
5109 if (*p == '\\') {
5110 /* naked back slash */
5111 notescaped = 0;
5112 goto copy;
5113 }
5114 if (*p == CTLESC) {
5115 p++;
5116 if (notescaped && inquotes && *p != '/') {
5117 *q++ = '\\';
5118 }
5119 }
5120 notescaped = globbing;
5121 copy:
5122 *q++ = *p++;
5123 }
5124 *q = '\0';
5125 if (flag & RMESCAPE_GROW) {
5126 expdest = r;
5127 STADJUST(q - r + 1, expdest);
5128 }
5129 return r;
5130}
5131#define rmescapes(p) _rmescapes((p), 0)
5132
5133#define pmatch(a, b) !fnmatch((a), (b), 0)
5134
5135/*
5136 * Prepare a pattern for a expmeta (internal glob(3)) call.
5137 *
5138 * Returns an stalloced string.
5139 */
5140static char *
5141preglob(const char *pattern, int quoted, int flag)
5142{
5143 flag |= RMESCAPE_GLOB;
5144 if (quoted) {
5145 flag |= RMESCAPE_QUOTED;
5146 }
5147 return _rmescapes((char *)pattern, flag);
5148}
5149
5150/*
5151 * Put a string on the stack.
5152 */
5153static void
5154memtodest(const char *p, size_t len, int syntax, int quotes)
5155{
5156 char *q = expdest;
5157
5158 q = makestrspace(len * 2, q);
5159
5160 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005161 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005162 if (!c)
5163 continue;
5164 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5165 USTPUTC(CTLESC, q);
5166 USTPUTC(c, q);
5167 }
5168
5169 expdest = q;
5170}
5171
5172static void
5173strtodest(const char *p, int syntax, int quotes)
5174{
5175 memtodest(p, strlen(p), syntax, quotes);
5176}
5177
5178/*
5179 * Record the fact that we have to scan this region of the
5180 * string for IFS characters.
5181 */
5182static void
5183recordregion(int start, int end, int nulonly)
5184{
5185 struct ifsregion *ifsp;
5186
5187 if (ifslastp == NULL) {
5188 ifsp = &ifsfirst;
5189 } else {
5190 INT_OFF;
5191 ifsp = ckmalloc(sizeof(*ifsp));
5192 ifsp->next = NULL;
5193 ifslastp->next = ifsp;
5194 INT_ON;
5195 }
5196 ifslastp = ifsp;
5197 ifslastp->begoff = start;
5198 ifslastp->endoff = end;
5199 ifslastp->nulonly = nulonly;
5200}
5201
5202static void
5203removerecordregions(int endoff)
5204{
5205 if (ifslastp == NULL)
5206 return;
5207
5208 if (ifsfirst.endoff > endoff) {
5209 while (ifsfirst.next != NULL) {
5210 struct ifsregion *ifsp;
5211 INT_OFF;
5212 ifsp = ifsfirst.next->next;
5213 free(ifsfirst.next);
5214 ifsfirst.next = ifsp;
5215 INT_ON;
5216 }
5217 if (ifsfirst.begoff > endoff)
5218 ifslastp = NULL;
5219 else {
5220 ifslastp = &ifsfirst;
5221 ifsfirst.endoff = endoff;
5222 }
5223 return;
5224 }
5225
5226 ifslastp = &ifsfirst;
5227 while (ifslastp->next && ifslastp->next->begoff < endoff)
5228 ifslastp=ifslastp->next;
5229 while (ifslastp->next != NULL) {
5230 struct ifsregion *ifsp;
5231 INT_OFF;
5232 ifsp = ifslastp->next->next;
5233 free(ifslastp->next);
5234 ifslastp->next = ifsp;
5235 INT_ON;
5236 }
5237 if (ifslastp->endoff > endoff)
5238 ifslastp->endoff = endoff;
5239}
5240
5241static char *
5242exptilde(char *startp, char *p, int flag)
5243{
5244 char c;
5245 char *name;
5246 struct passwd *pw;
5247 const char *home;
5248 int quotes = flag & (EXP_FULL | EXP_CASE);
5249 int startloc;
5250
5251 name = p + 1;
5252
5253 while ((c = *++p) != '\0') {
5254 switch (c) {
5255 case CTLESC:
5256 return startp;
5257 case CTLQUOTEMARK:
5258 return startp;
5259 case ':':
5260 if (flag & EXP_VARTILDE)
5261 goto done;
5262 break;
5263 case '/':
5264 case CTLENDVAR:
5265 goto done;
5266 }
5267 }
5268 done:
5269 *p = '\0';
5270 if (*name == '\0') {
5271 home = lookupvar(homestr);
5272 } else {
5273 pw = getpwnam(name);
5274 if (pw == NULL)
5275 goto lose;
5276 home = pw->pw_dir;
5277 }
5278 if (!home || !*home)
5279 goto lose;
5280 *p = c;
5281 startloc = expdest - (char *)stackblock();
5282 strtodest(home, SQSYNTAX, quotes);
5283 recordregion(startloc, expdest - (char *)stackblock(), 0);
5284 return p;
5285 lose:
5286 *p = c;
5287 return startp;
5288}
5289
5290/*
5291 * Execute a command inside back quotes. If it's a builtin command, we
5292 * want to save its output in a block obtained from malloc. Otherwise
5293 * we fork off a subprocess and get the output of the command via a pipe.
5294 * Should be called with interrupts off.
5295 */
5296struct backcmd { /* result of evalbackcmd */
5297 int fd; /* file descriptor to read from */
5298 char *buf; /* buffer */
5299 int nleft; /* number of chars in buffer */
5300 struct job *jp; /* job structure for command */
5301};
5302
5303/* These forward decls are needed to use "eval" code for backticks handling: */
5304static int back_exitstatus; /* exit status of backquoted command */
5305#define EV_EXIT 01 /* exit after evaluating tree */
5306static void evaltree(union node *, int);
5307
5308static void
5309evalbackcmd(union node *n, struct backcmd *result)
5310{
5311 int saveherefd;
5312
5313 result->fd = -1;
5314 result->buf = NULL;
5315 result->nleft = 0;
5316 result->jp = NULL;
5317 if (n == NULL) {
5318 goto out;
5319 }
5320
5321 saveherefd = herefd;
5322 herefd = -1;
5323
5324 {
5325 int pip[2];
5326 struct job *jp;
5327
5328 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005329 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005330 jp = makejob(n, 1);
5331 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5332 FORCE_INT_ON;
5333 close(pip[0]);
5334 if (pip[1] != 1) {
5335 close(1);
5336 copyfd(pip[1], 1);
5337 close(pip[1]);
5338 }
5339 eflag = 0;
5340 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5341 /* NOTREACHED */
5342 }
5343 close(pip[1]);
5344 result->fd = pip[0];
5345 result->jp = jp;
5346 }
5347 herefd = saveherefd;
5348 out:
5349 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5350 result->fd, result->buf, result->nleft, result->jp));
5351}
5352
5353/*
5354 * Expand stuff in backwards quotes.
5355 */
5356static void
5357expbackq(union node *cmd, int quoted, int quotes)
5358{
5359 struct backcmd in;
5360 int i;
5361 char buf[128];
5362 char *p;
5363 char *dest;
5364 int startloc;
5365 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5366 struct stackmark smark;
5367
5368 INT_OFF;
5369 setstackmark(&smark);
5370 dest = expdest;
5371 startloc = dest - (char *)stackblock();
5372 grabstackstr(dest);
5373 evalbackcmd(cmd, &in);
5374 popstackmark(&smark);
5375
5376 p = in.buf;
5377 i = in.nleft;
5378 if (i == 0)
5379 goto read;
5380 for (;;) {
5381 memtodest(p, i, syntax, quotes);
5382 read:
5383 if (in.fd < 0)
5384 break;
5385 i = safe_read(in.fd, buf, sizeof(buf));
5386 TRACE(("expbackq: read returns %d\n", i));
5387 if (i <= 0)
5388 break;
5389 p = buf;
5390 }
5391
5392 if (in.buf)
5393 free(in.buf);
5394 if (in.fd >= 0) {
5395 close(in.fd);
5396 back_exitstatus = waitforjob(in.jp);
5397 }
5398 INT_ON;
5399
5400 /* Eat all trailing newlines */
5401 dest = expdest;
5402 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5403 STUNPUTC(dest);
5404 expdest = dest;
5405
5406 if (quoted == 0)
5407 recordregion(startloc, dest - (char *)stackblock(), 0);
5408 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5409 (dest - (char *)stackblock()) - startloc,
5410 (dest - (char *)stackblock()) - startloc,
5411 stackblock() + startloc));
5412}
5413
5414#if ENABLE_ASH_MATH_SUPPORT
5415/*
5416 * Expand arithmetic expression. Backup to start of expression,
5417 * evaluate, place result in (backed up) result, adjust string position.
5418 */
5419static void
5420expari(int quotes)
5421{
5422 char *p, *start;
5423 int begoff;
5424 int flag;
5425 int len;
5426
5427 /* ifsfree(); */
5428
5429 /*
5430 * This routine is slightly over-complicated for
5431 * efficiency. Next we scan backwards looking for the
5432 * start of arithmetic.
5433 */
5434 start = stackblock();
5435 p = expdest - 1;
5436 *p = '\0';
5437 p--;
5438 do {
5439 int esc;
5440
5441 while (*p != CTLARI) {
5442 p--;
5443#if DEBUG
5444 if (p < start) {
5445 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5446 }
5447#endif
5448 }
5449
5450 esc = esclen(start, p);
5451 if (!(esc % 2)) {
5452 break;
5453 }
5454
5455 p -= esc + 1;
5456 } while (1);
5457
5458 begoff = p - start;
5459
5460 removerecordregions(begoff);
5461
5462 flag = p[1];
5463
5464 expdest = p;
5465
5466 if (quotes)
5467 rmescapes(p + 2);
5468
5469 len = cvtnum(dash_arith(p + 2));
5470
5471 if (flag != '"')
5472 recordregion(begoff, begoff + len, 0);
5473}
5474#endif
5475
5476/* argstr needs it */
5477static char *evalvar(char *p, int flag);
5478
5479/*
5480 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5481 * characters to allow for further processing. Otherwise treat
5482 * $@ like $* since no splitting will be performed.
5483 */
5484static void
5485argstr(char *p, int flag)
5486{
5487 static const char spclchars[] = {
5488 '=',
5489 ':',
5490 CTLQUOTEMARK,
5491 CTLENDVAR,
5492 CTLESC,
5493 CTLVAR,
5494 CTLBACKQ,
5495 CTLBACKQ | CTLQUOTE,
5496#if ENABLE_ASH_MATH_SUPPORT
5497 CTLENDARI,
5498#endif
5499 0
5500 };
5501 const char *reject = spclchars;
5502 int c;
5503 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5504 int breakall = flag & EXP_WORD;
5505 int inquotes;
5506 size_t length;
5507 int startloc;
5508
5509 if (!(flag & EXP_VARTILDE)) {
5510 reject += 2;
5511 } else if (flag & EXP_VARTILDE2) {
5512 reject++;
5513 }
5514 inquotes = 0;
5515 length = 0;
5516 if (flag & EXP_TILDE) {
5517 char *q;
5518
5519 flag &= ~EXP_TILDE;
5520 tilde:
5521 q = p;
5522 if (*q == CTLESC && (flag & EXP_QWORD))
5523 q++;
5524 if (*q == '~')
5525 p = exptilde(p, q, flag);
5526 }
5527 start:
5528 startloc = expdest - (char *)stackblock();
5529 for (;;) {
5530 length += strcspn(p + length, reject);
5531 c = p[length];
5532 if (c && (!(c & 0x80)
5533#if ENABLE_ASH_MATH_SUPPORT
5534 || c == CTLENDARI
5535#endif
5536 )) {
5537 /* c == '=' || c == ':' || c == CTLENDARI */
5538 length++;
5539 }
5540 if (length > 0) {
5541 int newloc;
5542 expdest = stack_nputstr(p, length, expdest);
5543 newloc = expdest - (char *)stackblock();
5544 if (breakall && !inquotes && newloc > startloc) {
5545 recordregion(startloc, newloc, 0);
5546 }
5547 startloc = newloc;
5548 }
5549 p += length + 1;
5550 length = 0;
5551
5552 switch (c) {
5553 case '\0':
5554 goto breakloop;
5555 case '=':
5556 if (flag & EXP_VARTILDE2) {
5557 p--;
5558 continue;
5559 }
5560 flag |= EXP_VARTILDE2;
5561 reject++;
5562 /* fall through */
5563 case ':':
5564 /*
5565 * sort of a hack - expand tildes in variable
5566 * assignments (after the first '=' and after ':'s).
5567 */
5568 if (*--p == '~') {
5569 goto tilde;
5570 }
5571 continue;
5572 }
5573
5574 switch (c) {
5575 case CTLENDVAR: /* ??? */
5576 goto breakloop;
5577 case CTLQUOTEMARK:
5578 /* "$@" syntax adherence hack */
5579 if (
5580 !inquotes &&
5581 !memcmp(p, dolatstr, 4) &&
5582 (p[4] == CTLQUOTEMARK || (
5583 p[4] == CTLENDVAR &&
5584 p[5] == CTLQUOTEMARK
5585 ))
5586 ) {
5587 p = evalvar(p + 1, flag) + 1;
5588 goto start;
5589 }
5590 inquotes = !inquotes;
5591 addquote:
5592 if (quotes) {
5593 p--;
5594 length++;
5595 startloc++;
5596 }
5597 break;
5598 case CTLESC:
5599 startloc++;
5600 length++;
5601 goto addquote;
5602 case CTLVAR:
5603 p = evalvar(p, flag);
5604 goto start;
5605 case CTLBACKQ:
5606 c = 0;
5607 case CTLBACKQ|CTLQUOTE:
5608 expbackq(argbackq->n, c, quotes);
5609 argbackq = argbackq->next;
5610 goto start;
5611#if ENABLE_ASH_MATH_SUPPORT
5612 case CTLENDARI:
5613 p--;
5614 expari(quotes);
5615 goto start;
5616#endif
5617 }
5618 }
5619 breakloop:
5620 ;
5621}
5622
5623static char *
5624scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5625 int zero)
5626{
5627 char *loc;
5628 char *loc2;
5629 char c;
5630
5631 loc = startp;
5632 loc2 = rmesc;
5633 do {
5634 int match;
5635 const char *s = loc2;
5636 c = *loc2;
5637 if (zero) {
5638 *loc2 = '\0';
5639 s = rmesc;
5640 }
5641 match = pmatch(str, s);
5642 *loc2 = c;
5643 if (match)
5644 return loc;
5645 if (quotes && *loc == CTLESC)
5646 loc++;
5647 loc++;
5648 loc2++;
5649 } while (c);
5650 return 0;
5651}
5652
5653static char *
5654scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5655 int zero)
5656{
5657 int esc = 0;
5658 char *loc;
5659 char *loc2;
5660
5661 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5662 int match;
5663 char c = *loc2;
5664 const char *s = loc2;
5665 if (zero) {
5666 *loc2 = '\0';
5667 s = rmesc;
5668 }
5669 match = pmatch(str, s);
5670 *loc2 = c;
5671 if (match)
5672 return loc;
5673 loc--;
5674 if (quotes) {
5675 if (--esc < 0) {
5676 esc = esclen(startp, loc);
5677 }
5678 if (esc % 2) {
5679 esc--;
5680 loc--;
5681 }
5682 }
5683 }
5684 return 0;
5685}
5686
5687static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5688static void
5689varunset(const char *end, const char *var, const char *umsg, int varflags)
5690{
5691 const char *msg;
5692 const char *tail;
5693
5694 tail = nullstr;
5695 msg = "parameter not set";
5696 if (umsg) {
5697 if (*end == CTLENDVAR) {
5698 if (varflags & VSNUL)
5699 tail = " or null";
5700 } else
5701 msg = umsg;
5702 }
5703 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5704}
5705
5706static const char *
5707subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5708{
5709 char *startp;
5710 char *loc;
5711 int saveherefd = herefd;
5712 struct nodelist *saveargbackq = argbackq;
5713 int amount;
5714 char *rmesc, *rmescend;
5715 int zero;
5716 char *(*scan)(char *, char *, char *, char *, int , int);
5717
5718 herefd = -1;
5719 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5720 STPUTC('\0', expdest);
5721 herefd = saveherefd;
5722 argbackq = saveargbackq;
5723 startp = stackblock() + startloc;
5724
5725 switch (subtype) {
5726 case VSASSIGN:
5727 setvar(str, startp, 0);
5728 amount = startp - expdest;
5729 STADJUST(amount, expdest);
5730 return startp;
5731
5732 case VSQUESTION:
5733 varunset(p, str, startp, varflags);
5734 /* NOTREACHED */
5735 }
5736
5737 subtype -= VSTRIMRIGHT;
5738#if DEBUG
5739 if (subtype < 0 || subtype > 3)
5740 abort();
5741#endif
5742
5743 rmesc = startp;
5744 rmescend = stackblock() + strloc;
5745 if (quotes) {
5746 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5747 if (rmesc != startp) {
5748 rmescend = expdest;
5749 startp = stackblock() + startloc;
5750 }
5751 }
5752 rmescend--;
5753 str = stackblock() + strloc;
5754 preglob(str, varflags & VSQUOTE, 0);
5755
5756 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5757 zero = subtype >> 1;
5758 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5759 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5760
5761 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5762 if (loc) {
5763 if (zero) {
5764 memmove(startp, loc, str - loc);
5765 loc = startp + (str - loc) - 1;
5766 }
5767 *loc = '\0';
5768 amount = loc - expdest;
5769 STADJUST(amount, expdest);
5770 }
5771 return loc;
5772}
5773
5774/*
5775 * Add the value of a specialized variable to the stack string.
5776 */
5777static ssize_t
5778varvalue(char *name, int varflags, int flags)
5779{
5780 int num;
5781 char *p;
5782 int i;
5783 int sep = 0;
5784 int sepq = 0;
5785 ssize_t len = 0;
5786 char **ap;
5787 int syntax;
5788 int quoted = varflags & VSQUOTE;
5789 int subtype = varflags & VSTYPE;
5790 int quotes = flags & (EXP_FULL | EXP_CASE);
5791
5792 if (quoted && (flags & EXP_FULL))
5793 sep = 1 << CHAR_BIT;
5794
5795 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5796 switch (*name) {
5797 case '$':
5798 num = rootpid;
5799 goto numvar;
5800 case '?':
5801 num = exitstatus;
5802 goto numvar;
5803 case '#':
5804 num = shellparam.nparam;
5805 goto numvar;
5806 case '!':
5807 num = backgndpid;
5808 if (num == 0)
5809 return -1;
5810 numvar:
5811 len = cvtnum(num);
5812 break;
5813 case '-':
5814 p = makestrspace(NOPTS, expdest);
5815 for (i = NOPTS - 1; i >= 0; i--) {
5816 if (optlist[i]) {
5817 USTPUTC(optletters(i), p);
5818 len++;
5819 }
5820 }
5821 expdest = p;
5822 break;
5823 case '@':
5824 if (sep)
5825 goto param;
5826 /* fall through */
5827 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005828 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005829 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5830 sepq = 1;
5831 param:
5832 ap = shellparam.p;
5833 if (!ap)
5834 return -1;
5835 while ((p = *ap++)) {
5836 size_t partlen;
5837
5838 partlen = strlen(p);
5839 len += partlen;
5840
5841 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5842 memtodest(p, partlen, syntax, quotes);
5843
5844 if (*ap && sep) {
5845 char *q;
5846
5847 len++;
5848 if (subtype == VSPLUS || subtype == VSLENGTH) {
5849 continue;
5850 }
5851 q = expdest;
5852 if (sepq)
5853 STPUTC(CTLESC, q);
5854 STPUTC(sep, q);
5855 expdest = q;
5856 }
5857 }
5858 return len;
5859 case '0':
5860 case '1':
5861 case '2':
5862 case '3':
5863 case '4':
5864 case '5':
5865 case '6':
5866 case '7':
5867 case '8':
5868 case '9':
5869 num = atoi(name);
5870 if (num < 0 || num > shellparam.nparam)
5871 return -1;
5872 p = num ? shellparam.p[num - 1] : arg0;
5873 goto value;
5874 default:
5875 p = lookupvar(name);
5876 value:
5877 if (!p)
5878 return -1;
5879
5880 len = strlen(p);
5881 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5882 memtodest(p, len, syntax, quotes);
5883 return len;
5884 }
5885
5886 if (subtype == VSPLUS || subtype == VSLENGTH)
5887 STADJUST(-len, expdest);
5888 return len;
5889}
5890
5891/*
5892 * Expand a variable, and return a pointer to the next character in the
5893 * input string.
5894 */
5895static char *
5896evalvar(char *p, int flag)
5897{
5898 int subtype;
5899 int varflags;
5900 char *var;
5901 int patloc;
5902 int c;
5903 int startloc;
5904 ssize_t varlen;
5905 int easy;
5906 int quotes;
5907 int quoted;
5908
5909 quotes = flag & (EXP_FULL | EXP_CASE);
5910 varflags = *p++;
5911 subtype = varflags & VSTYPE;
5912 quoted = varflags & VSQUOTE;
5913 var = p;
5914 easy = (!quoted || (*var == '@' && shellparam.nparam));
5915 startloc = expdest - (char *)stackblock();
5916 p = strchr(p, '=') + 1;
5917
5918 again:
5919 varlen = varvalue(var, varflags, flag);
5920 if (varflags & VSNUL)
5921 varlen--;
5922
5923 if (subtype == VSPLUS) {
5924 varlen = -1 - varlen;
5925 goto vsplus;
5926 }
5927
5928 if (subtype == VSMINUS) {
5929 vsplus:
5930 if (varlen < 0) {
5931 argstr(
5932 p, flag | EXP_TILDE |
5933 (quoted ? EXP_QWORD : EXP_WORD)
5934 );
5935 goto end;
5936 }
5937 if (easy)
5938 goto record;
5939 goto end;
5940 }
5941
5942 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5943 if (varlen < 0) {
5944 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5945 varflags &= ~VSNUL;
5946 /*
5947 * Remove any recorded regions beyond
5948 * start of variable
5949 */
5950 removerecordregions(startloc);
5951 goto again;
5952 }
5953 goto end;
5954 }
5955 if (easy)
5956 goto record;
5957 goto end;
5958 }
5959
5960 if (varlen < 0 && uflag)
5961 varunset(p, var, 0, 0);
5962
5963 if (subtype == VSLENGTH) {
5964 cvtnum(varlen > 0 ? varlen : 0);
5965 goto record;
5966 }
5967
5968 if (subtype == VSNORMAL) {
5969 if (!easy)
5970 goto end;
5971 record:
5972 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5973 goto end;
5974 }
5975
5976#if DEBUG
5977 switch (subtype) {
5978 case VSTRIMLEFT:
5979 case VSTRIMLEFTMAX:
5980 case VSTRIMRIGHT:
5981 case VSTRIMRIGHTMAX:
5982 break;
5983 default:
5984 abort();
5985 }
5986#endif
5987
5988 if (varlen >= 0) {
5989 /*
5990 * Terminate the string and start recording the pattern
5991 * right after it
5992 */
5993 STPUTC('\0', expdest);
5994 patloc = expdest - (char *)stackblock();
5995 if (subevalvar(p, NULL, patloc, subtype,
5996 startloc, varflags, quotes) == 0) {
5997 int amount = expdest - (
5998 (char *)stackblock() + patloc - 1
5999 );
6000 STADJUST(-amount, expdest);
6001 }
6002 /* Remove any recorded regions beyond start of variable */
6003 removerecordregions(startloc);
6004 goto record;
6005 }
6006
6007 end:
6008 if (subtype != VSNORMAL) { /* skip to end of alternative */
6009 int nesting = 1;
6010 for (;;) {
6011 c = *p++;
6012 if (c == CTLESC)
6013 p++;
6014 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6015 if (varlen >= 0)
6016 argbackq = argbackq->next;
6017 } else if (c == CTLVAR) {
6018 if ((*p++ & VSTYPE) != VSNORMAL)
6019 nesting++;
6020 } else if (c == CTLENDVAR) {
6021 if (--nesting == 0)
6022 break;
6023 }
6024 }
6025 }
6026 return p;
6027}
6028
6029/*
6030 * Break the argument string into pieces based upon IFS and add the
6031 * strings to the argument list. The regions of the string to be
6032 * searched for IFS characters have been stored by recordregion.
6033 */
6034static void
6035ifsbreakup(char *string, struct arglist *arglist)
6036{
6037 struct ifsregion *ifsp;
6038 struct strlist *sp;
6039 char *start;
6040 char *p;
6041 char *q;
6042 const char *ifs, *realifs;
6043 int ifsspc;
6044 int nulonly;
6045
6046 start = string;
6047 if (ifslastp != NULL) {
6048 ifsspc = 0;
6049 nulonly = 0;
6050 realifs = ifsset() ? ifsval() : defifs;
6051 ifsp = &ifsfirst;
6052 do {
6053 p = string + ifsp->begoff;
6054 nulonly = ifsp->nulonly;
6055 ifs = nulonly ? nullstr : realifs;
6056 ifsspc = 0;
6057 while (p < string + ifsp->endoff) {
6058 q = p;
6059 if (*p == CTLESC)
6060 p++;
6061 if (!strchr(ifs, *p)) {
6062 p++;
6063 continue;
6064 }
6065 if (!nulonly)
6066 ifsspc = (strchr(defifs, *p) != NULL);
6067 /* Ignore IFS whitespace at start */
6068 if (q == start && ifsspc) {
6069 p++;
6070 start = p;
6071 continue;
6072 }
6073 *q = '\0';
6074 sp = stalloc(sizeof(*sp));
6075 sp->text = start;
6076 *arglist->lastp = sp;
6077 arglist->lastp = &sp->next;
6078 p++;
6079 if (!nulonly) {
6080 for (;;) {
6081 if (p >= string + ifsp->endoff) {
6082 break;
6083 }
6084 q = p;
6085 if (*p == CTLESC)
6086 p++;
6087 if (strchr(ifs, *p) == NULL ) {
6088 p = q;
6089 break;
6090 } else if (strchr(defifs, *p) == NULL) {
6091 if (ifsspc) {
6092 p++;
6093 ifsspc = 0;
6094 } else {
6095 p = q;
6096 break;
6097 }
6098 } else
6099 p++;
6100 }
6101 }
6102 start = p;
6103 } /* while */
6104 ifsp = ifsp->next;
6105 } while (ifsp != NULL);
6106 if (nulonly)
6107 goto add;
6108 }
6109
6110 if (!*start)
6111 return;
6112
6113 add:
6114 sp = stalloc(sizeof(*sp));
6115 sp->text = start;
6116 *arglist->lastp = sp;
6117 arglist->lastp = &sp->next;
6118}
6119
6120static void
6121ifsfree(void)
6122{
6123 struct ifsregion *p;
6124
6125 INT_OFF;
6126 p = ifsfirst.next;
6127 do {
6128 struct ifsregion *ifsp;
6129 ifsp = p->next;
6130 free(p);
6131 p = ifsp;
6132 } while (p);
6133 ifslastp = NULL;
6134 ifsfirst.next = NULL;
6135 INT_ON;
6136}
6137
6138/*
6139 * Add a file name to the list.
6140 */
6141static void
6142addfname(const char *name)
6143{
6144 struct strlist *sp;
6145
6146 sp = stalloc(sizeof(*sp));
6147 sp->text = ststrdup(name);
6148 *exparg.lastp = sp;
6149 exparg.lastp = &sp->next;
6150}
6151
6152static char *expdir;
6153
6154/*
6155 * Do metacharacter (i.e. *, ?, [...]) expansion.
6156 */
6157static void
6158expmeta(char *enddir, char *name)
6159{
6160 char *p;
6161 const char *cp;
6162 char *start;
6163 char *endname;
6164 int metaflag;
6165 struct stat statb;
6166 DIR *dirp;
6167 struct dirent *dp;
6168 int atend;
6169 int matchdot;
6170
6171 metaflag = 0;
6172 start = name;
6173 for (p = name; *p; p++) {
6174 if (*p == '*' || *p == '?')
6175 metaflag = 1;
6176 else if (*p == '[') {
6177 char *q = p + 1;
6178 if (*q == '!')
6179 q++;
6180 for (;;) {
6181 if (*q == '\\')
6182 q++;
6183 if (*q == '/' || *q == '\0')
6184 break;
6185 if (*++q == ']') {
6186 metaflag = 1;
6187 break;
6188 }
6189 }
6190 } else if (*p == '\\')
6191 p++;
6192 else if (*p == '/') {
6193 if (metaflag)
6194 goto out;
6195 start = p + 1;
6196 }
6197 }
6198 out:
6199 if (metaflag == 0) { /* we've reached the end of the file name */
6200 if (enddir != expdir)
6201 metaflag++;
6202 p = name;
6203 do {
6204 if (*p == '\\')
6205 p++;
6206 *enddir++ = *p;
6207 } while (*p++);
6208 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6209 addfname(expdir);
6210 return;
6211 }
6212 endname = p;
6213 if (name < start) {
6214 p = name;
6215 do {
6216 if (*p == '\\')
6217 p++;
6218 *enddir++ = *p++;
6219 } while (p < start);
6220 }
6221 if (enddir == expdir) {
6222 cp = ".";
6223 } else if (enddir == expdir + 1 && *expdir == '/') {
6224 cp = "/";
6225 } else {
6226 cp = expdir;
6227 enddir[-1] = '\0';
6228 }
6229 dirp = opendir(cp);
6230 if (dirp == NULL)
6231 return;
6232 if (enddir != expdir)
6233 enddir[-1] = '/';
6234 if (*endname == 0) {
6235 atend = 1;
6236 } else {
6237 atend = 0;
6238 *endname++ = '\0';
6239 }
6240 matchdot = 0;
6241 p = start;
6242 if (*p == '\\')
6243 p++;
6244 if (*p == '.')
6245 matchdot++;
6246 while (! intpending && (dp = readdir(dirp)) != NULL) {
6247 if (dp->d_name[0] == '.' && ! matchdot)
6248 continue;
6249 if (pmatch(start, dp->d_name)) {
6250 if (atend) {
6251 strcpy(enddir, dp->d_name);
6252 addfname(expdir);
6253 } else {
6254 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6255 continue;
6256 p[-1] = '/';
6257 expmeta(p, endname);
6258 }
6259 }
6260 }
6261 closedir(dirp);
6262 if (! atend)
6263 endname[-1] = '/';
6264}
6265
6266static struct strlist *
6267msort(struct strlist *list, int len)
6268{
6269 struct strlist *p, *q = NULL;
6270 struct strlist **lpp;
6271 int half;
6272 int n;
6273
6274 if (len <= 1)
6275 return list;
6276 half = len >> 1;
6277 p = list;
6278 for (n = half; --n >= 0; ) {
6279 q = p;
6280 p = p->next;
6281 }
6282 q->next = NULL; /* terminate first half of list */
6283 q = msort(list, half); /* sort first half of list */
6284 p = msort(p, len - half); /* sort second half */
6285 lpp = &list;
6286 for (;;) {
6287#if ENABLE_LOCALE_SUPPORT
6288 if (strcoll(p->text, q->text) < 0)
6289#else
6290 if (strcmp(p->text, q->text) < 0)
6291#endif
6292 {
6293 *lpp = p;
6294 lpp = &p->next;
6295 p = *lpp;
6296 if (p == NULL) {
6297 *lpp = q;
6298 break;
6299 }
6300 } else {
6301 *lpp = q;
6302 lpp = &q->next;
6303 q = *lpp;
6304 if (q == NULL) {
6305 *lpp = p;
6306 break;
6307 }
6308 }
6309 }
6310 return list;
6311}
6312
6313/*
6314 * Sort the results of file name expansion. It calculates the number of
6315 * strings to sort and then calls msort (short for merge sort) to do the
6316 * work.
6317 */
6318static struct strlist *
6319expsort(struct strlist *str)
6320{
6321 int len;
6322 struct strlist *sp;
6323
6324 len = 0;
6325 for (sp = str; sp; sp = sp->next)
6326 len++;
6327 return msort(str, len);
6328}
6329
6330static void
6331expandmeta(struct strlist *str, int flag)
6332{
6333 static const char metachars[] = {
6334 '*', '?', '[', 0
6335 };
6336 /* TODO - EXP_REDIR */
6337
6338 while (str) {
6339 struct strlist **savelastp;
6340 struct strlist *sp;
6341 char *p;
6342
6343 if (fflag)
6344 goto nometa;
6345 if (!strpbrk(str->text, metachars))
6346 goto nometa;
6347 savelastp = exparg.lastp;
6348
6349 INT_OFF;
6350 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6351 {
6352 int i = strlen(str->text);
6353 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6354 }
6355
6356 expmeta(expdir, p);
6357 free(expdir);
6358 if (p != str->text)
6359 free(p);
6360 INT_ON;
6361 if (exparg.lastp == savelastp) {
6362 /*
6363 * no matches
6364 */
6365 nometa:
6366 *exparg.lastp = str;
6367 rmescapes(str->text);
6368 exparg.lastp = &str->next;
6369 } else {
6370 *exparg.lastp = NULL;
6371 *savelastp = sp = expsort(*savelastp);
6372 while (sp->next != NULL)
6373 sp = sp->next;
6374 exparg.lastp = &sp->next;
6375 }
6376 str = str->next;
6377 }
6378}
6379
6380/*
6381 * Perform variable substitution and command substitution on an argument,
6382 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6383 * perform splitting and file name expansion. When arglist is NULL, perform
6384 * here document expansion.
6385 */
6386static void
6387expandarg(union node *arg, struct arglist *arglist, int flag)
6388{
6389 struct strlist *sp;
6390 char *p;
6391
6392 argbackq = arg->narg.backquote;
6393 STARTSTACKSTR(expdest);
6394 ifsfirst.next = NULL;
6395 ifslastp = NULL;
6396 argstr(arg->narg.text, flag);
6397 p = _STPUTC('\0', expdest);
6398 expdest = p - 1;
6399 if (arglist == NULL) {
6400 return; /* here document expanded */
6401 }
6402 p = grabstackstr(p);
6403 exparg.lastp = &exparg.list;
6404 /*
6405 * TODO - EXP_REDIR
6406 */
6407 if (flag & EXP_FULL) {
6408 ifsbreakup(p, &exparg);
6409 *exparg.lastp = NULL;
6410 exparg.lastp = &exparg.list;
6411 expandmeta(exparg.list, flag);
6412 } else {
6413 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6414 rmescapes(p);
6415 sp = stalloc(sizeof(*sp));
6416 sp->text = p;
6417 *exparg.lastp = sp;
6418 exparg.lastp = &sp->next;
6419 }
6420 if (ifsfirst.next)
6421 ifsfree();
6422 *exparg.lastp = NULL;
6423 if (exparg.list) {
6424 *arglist->lastp = exparg.list;
6425 arglist->lastp = exparg.lastp;
6426 }
6427}
6428
6429/*
6430 * Expand shell variables and backquotes inside a here document.
6431 */
6432static void
6433expandhere(union node *arg, int fd)
6434{
6435 herefd = fd;
6436 expandarg(arg, (struct arglist *)NULL, 0);
6437 full_write(fd, stackblock(), expdest - (char *)stackblock());
6438}
6439
6440/*
6441 * Returns true if the pattern matches the string.
6442 */
6443static int
6444patmatch(char *pattern, const char *string)
6445{
6446 return pmatch(preglob(pattern, 0, 0), string);
6447}
6448
6449/*
6450 * See if a pattern matches in a case statement.
6451 */
6452static int
6453casematch(union node *pattern, char *val)
6454{
6455 struct stackmark smark;
6456 int result;
6457
6458 setstackmark(&smark);
6459 argbackq = pattern->narg.backquote;
6460 STARTSTACKSTR(expdest);
6461 ifslastp = NULL;
6462 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6463 STACKSTRNUL(expdest);
6464 result = patmatch(stackblock(), val);
6465 popstackmark(&smark);
6466 return result;
6467}
6468
6469
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006470/* ============ find_command */
6471
6472struct builtincmd {
6473 const char *name;
6474 int (*builtin)(int, char **);
6475 /* unsigned flags; */
6476};
6477#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6478#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6479#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6480
6481struct cmdentry {
6482 int cmdtype;
6483 union param {
6484 int index;
6485 const struct builtincmd *cmd;
6486 struct funcnode *func;
6487 } u;
6488};
6489/* values of cmdtype */
6490#define CMDUNKNOWN -1 /* no entry in table for command */
6491#define CMDNORMAL 0 /* command is an executable program */
6492#define CMDFUNCTION 1 /* command is a shell function */
6493#define CMDBUILTIN 2 /* command is a shell builtin */
6494
6495/* action to find_command() */
6496#define DO_ERR 0x01 /* prints errors */
6497#define DO_ABS 0x02 /* checks absolute paths */
6498#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6499#define DO_ALTPATH 0x08 /* using alternate path */
6500#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6501
6502static void find_command(char *, struct cmdentry *, int, const char *);
6503
6504
6505/* ============ Hashing commands */
6506
6507/*
6508 * When commands are first encountered, they are entered in a hash table.
6509 * This ensures that a full path search will not have to be done for them
6510 * on each invocation.
6511 *
6512 * We should investigate converting to a linear search, even though that
6513 * would make the command name "hash" a misnomer.
6514 */
6515
6516#define CMDTABLESIZE 31 /* should be prime */
6517#define ARB 1 /* actual size determined at run time */
6518
6519struct tblentry {
6520 struct tblentry *next; /* next entry in hash chain */
6521 union param param; /* definition of builtin function */
6522 short cmdtype; /* index identifying command */
6523 char rehash; /* if set, cd done since entry created */
6524 char cmdname[ARB]; /* name of command */
6525};
6526
6527static struct tblentry *cmdtable[CMDTABLESIZE];
6528static int builtinloc = -1; /* index in path of %builtin, or -1 */
6529
6530static void
6531tryexec(char *cmd, char **argv, char **envp)
6532{
6533 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006534
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006535#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006536 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006537 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006538
6539 a = find_applet_by_name(cmd);
6540 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006541 if (a->noexec) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006542 current_applet = a;
Denis Vlasenkof5294e12007-04-14 10:09:57 +00006543 run_current_applet_and_exit(argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006544 }
6545 /* re-exec ourselves with the new arguments */
6546 execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
6547 /* If they called chroot or otherwise made the binary no longer
6548 * executable, fall through */
6549 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006550 }
6551#endif
6552
6553 repeat:
6554#ifdef SYSV
6555 do {
6556 execve(cmd, argv, envp);
6557 } while (errno == EINTR);
6558#else
6559 execve(cmd, argv, envp);
6560#endif
6561 if (repeated++) {
6562 free(argv);
6563 } else if (errno == ENOEXEC) {
6564 char **ap;
6565 char **new;
6566
6567 for (ap = argv; *ap; ap++)
6568 ;
6569 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6570 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006571 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006572 ap += 2;
6573 argv++;
6574 while ((*ap++ = *argv++))
6575 ;
6576 argv = new;
6577 goto repeat;
6578 }
6579}
6580
6581/*
6582 * Exec a program. Never returns. If you change this routine, you may
6583 * have to change the find_command routine as well.
6584 */
6585#define environment() listvars(VEXPORT, VUNSET, 0)
6586static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6587static void
6588shellexec(char **argv, const char *path, int idx)
6589{
6590 char *cmdname;
6591 int e;
6592 char **envp;
6593 int exerrno;
6594
6595 clearredir(1);
6596 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006597 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006598#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006599 || find_applet_by_name(argv[0])
6600#endif
6601 ) {
6602 tryexec(argv[0], argv, envp);
6603 e = errno;
6604 } else {
6605 e = ENOENT;
6606 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6607 if (--idx < 0 && pathopt == NULL) {
6608 tryexec(cmdname, argv, envp);
6609 if (errno != ENOENT && errno != ENOTDIR)
6610 e = errno;
6611 }
6612 stunalloc(cmdname);
6613 }
6614 }
6615
6616 /* Map to POSIX errors */
6617 switch (e) {
6618 case EACCES:
6619 exerrno = 126;
6620 break;
6621 case ENOENT:
6622 exerrno = 127;
6623 break;
6624 default:
6625 exerrno = 2;
6626 break;
6627 }
6628 exitstatus = exerrno;
6629 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6630 argv[0], e, suppressint ));
6631 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6632 /* NOTREACHED */
6633}
6634
6635static void
6636printentry(struct tblentry *cmdp)
6637{
6638 int idx;
6639 const char *path;
6640 char *name;
6641
6642 idx = cmdp->param.index;
6643 path = pathval();
6644 do {
6645 name = padvance(&path, cmdp->cmdname);
6646 stunalloc(name);
6647 } while (--idx >= 0);
6648 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6649}
6650
6651/*
6652 * Clear out command entries. The argument specifies the first entry in
6653 * PATH which has changed.
6654 */
6655static void
6656clearcmdentry(int firstchange)
6657{
6658 struct tblentry **tblp;
6659 struct tblentry **pp;
6660 struct tblentry *cmdp;
6661
6662 INT_OFF;
6663 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6664 pp = tblp;
6665 while ((cmdp = *pp) != NULL) {
6666 if ((cmdp->cmdtype == CMDNORMAL &&
6667 cmdp->param.index >= firstchange)
6668 || (cmdp->cmdtype == CMDBUILTIN &&
6669 builtinloc >= firstchange)
6670 ) {
6671 *pp = cmdp->next;
6672 free(cmdp);
6673 } else {
6674 pp = &cmdp->next;
6675 }
6676 }
6677 }
6678 INT_ON;
6679}
6680
6681/*
6682 * Locate a command in the command hash table. If "add" is nonzero,
6683 * add the command to the table if it is not already present. The
6684 * variable "lastcmdentry" is set to point to the address of the link
6685 * pointing to the entry, so that delete_cmd_entry can delete the
6686 * entry.
6687 *
6688 * Interrupts must be off if called with add != 0.
6689 */
6690static struct tblentry **lastcmdentry;
6691
6692static struct tblentry *
6693cmdlookup(const char *name, int add)
6694{
6695 unsigned int hashval;
6696 const char *p;
6697 struct tblentry *cmdp;
6698 struct tblentry **pp;
6699
6700 p = name;
6701 hashval = (unsigned char)*p << 4;
6702 while (*p)
6703 hashval += (unsigned char)*p++;
6704 hashval &= 0x7FFF;
6705 pp = &cmdtable[hashval % CMDTABLESIZE];
6706 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6707 if (strcmp(cmdp->cmdname, name) == 0)
6708 break;
6709 pp = &cmdp->next;
6710 }
6711 if (add && cmdp == NULL) {
6712 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6713 + strlen(name) + 1);
6714 cmdp->next = NULL;
6715 cmdp->cmdtype = CMDUNKNOWN;
6716 strcpy(cmdp->cmdname, name);
6717 }
6718 lastcmdentry = pp;
6719 return cmdp;
6720}
6721
6722/*
6723 * Delete the command entry returned on the last lookup.
6724 */
6725static void
6726delete_cmd_entry(void)
6727{
6728 struct tblentry *cmdp;
6729
6730 INT_OFF;
6731 cmdp = *lastcmdentry;
6732 *lastcmdentry = cmdp->next;
6733 if (cmdp->cmdtype == CMDFUNCTION)
6734 freefunc(cmdp->param.func);
6735 free(cmdp);
6736 INT_ON;
6737}
6738
6739/*
6740 * Add a new command entry, replacing any existing command entry for
6741 * the same name - except special builtins.
6742 */
6743static void
6744addcmdentry(char *name, struct cmdentry *entry)
6745{
6746 struct tblentry *cmdp;
6747
6748 cmdp = cmdlookup(name, 1);
6749 if (cmdp->cmdtype == CMDFUNCTION) {
6750 freefunc(cmdp->param.func);
6751 }
6752 cmdp->cmdtype = entry->cmdtype;
6753 cmdp->param = entry->u;
6754 cmdp->rehash = 0;
6755}
6756
6757static int
6758hashcmd(int argc, char **argv)
6759{
6760 struct tblentry **pp;
6761 struct tblentry *cmdp;
6762 int c;
6763 struct cmdentry entry;
6764 char *name;
6765
6766 while ((c = nextopt("r")) != '\0') {
6767 clearcmdentry(0);
6768 return 0;
6769 }
6770 if (*argptr == NULL) {
6771 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6772 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6773 if (cmdp->cmdtype == CMDNORMAL)
6774 printentry(cmdp);
6775 }
6776 }
6777 return 0;
6778 }
6779 c = 0;
6780 while ((name = *argptr) != NULL) {
6781 cmdp = cmdlookup(name, 0);
6782 if (cmdp != NULL
6783 && (cmdp->cmdtype == CMDNORMAL
6784 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6785 delete_cmd_entry();
6786 find_command(name, &entry, DO_ERR, pathval());
6787 if (entry.cmdtype == CMDUNKNOWN)
6788 c = 1;
6789 argptr++;
6790 }
6791 return c;
6792}
6793
6794/*
6795 * Called when a cd is done. Marks all commands so the next time they
6796 * are executed they will be rehashed.
6797 */
6798static void
6799hashcd(void)
6800{
6801 struct tblentry **pp;
6802 struct tblentry *cmdp;
6803
6804 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6805 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6806 if (cmdp->cmdtype == CMDNORMAL || (
6807 cmdp->cmdtype == CMDBUILTIN &&
6808 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6809 builtinloc > 0
6810 ))
6811 cmdp->rehash = 1;
6812 }
6813 }
6814}
6815
6816/*
6817 * Fix command hash table when PATH changed.
6818 * Called before PATH is changed. The argument is the new value of PATH;
6819 * pathval() still returns the old value at this point.
6820 * Called with interrupts off.
6821 */
6822static void
6823changepath(const char *newval)
6824{
6825 const char *old, *new;
6826 int idx;
6827 int firstchange;
6828 int idx_bltin;
6829
6830 old = pathval();
6831 new = newval;
6832 firstchange = 9999; /* assume no change */
6833 idx = 0;
6834 idx_bltin = -1;
6835 for (;;) {
6836 if (*old != *new) {
6837 firstchange = idx;
6838 if ((*old == '\0' && *new == ':')
6839 || (*old == ':' && *new == '\0'))
6840 firstchange++;
6841 old = new; /* ignore subsequent differences */
6842 }
6843 if (*new == '\0')
6844 break;
6845 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6846 idx_bltin = idx;
6847 if (*new == ':') {
6848 idx++;
6849 }
6850 new++, old++;
6851 }
6852 if (builtinloc < 0 && idx_bltin >= 0)
6853 builtinloc = idx_bltin; /* zap builtins */
6854 if (builtinloc >= 0 && idx_bltin < 0)
6855 firstchange = 0;
6856 clearcmdentry(firstchange);
6857 builtinloc = idx_bltin;
6858}
6859
6860#define TEOF 0
6861#define TNL 1
6862#define TREDIR 2
6863#define TWORD 3
6864#define TSEMI 4
6865#define TBACKGND 5
6866#define TAND 6
6867#define TOR 7
6868#define TPIPE 8
6869#define TLP 9
6870#define TRP 10
6871#define TENDCASE 11
6872#define TENDBQUOTE 12
6873#define TNOT 13
6874#define TCASE 14
6875#define TDO 15
6876#define TDONE 16
6877#define TELIF 17
6878#define TELSE 18
6879#define TESAC 19
6880#define TFI 20
6881#define TFOR 21
6882#define TIF 22
6883#define TIN 23
6884#define TTHEN 24
6885#define TUNTIL 25
6886#define TWHILE 26
6887#define TBEGIN 27
6888#define TEND 28
6889
6890/* first char is indicating which tokens mark the end of a list */
6891static const char *const tokname_array[] = {
6892 "\1end of file",
6893 "\0newline",
6894 "\0redirection",
6895 "\0word",
6896 "\0;",
6897 "\0&",
6898 "\0&&",
6899 "\0||",
6900 "\0|",
6901 "\0(",
6902 "\1)",
6903 "\1;;",
6904 "\1`",
6905#define KWDOFFSET 13
6906 /* the following are keywords */
6907 "\0!",
6908 "\0case",
6909 "\1do",
6910 "\1done",
6911 "\1elif",
6912 "\1else",
6913 "\1esac",
6914 "\1fi",
6915 "\0for",
6916 "\0if",
6917 "\0in",
6918 "\1then",
6919 "\0until",
6920 "\0while",
6921 "\0{",
6922 "\1}",
6923};
6924
6925static const char *
6926tokname(int tok)
6927{
6928 static char buf[16];
6929
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006930//try this:
6931//if (tok < TSEMI) return tokname_array[tok] + 1;
6932//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6933//return buf;
6934
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006935 if (tok >= TSEMI)
6936 buf[0] = '"';
6937 sprintf(buf + (tok >= TSEMI), "%s%c",
6938 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6939 return buf;
6940}
6941
6942/* Wrapper around strcmp for qsort/bsearch/... */
6943static int
6944pstrcmp(const void *a, const void *b)
6945{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006946 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006947}
6948
6949static const char *const *
6950findkwd(const char *s)
6951{
6952 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006953 (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
6954 sizeof(char *), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006955}
6956
6957/*
6958 * Locate and print what a word is...
6959 */
6960#if ENABLE_ASH_CMDCMD
6961static int
6962describe_command(char *command, int describe_command_verbose)
6963#else
6964#define describe_command_verbose 1
6965static int
6966describe_command(char *command)
6967#endif
6968{
6969 struct cmdentry entry;
6970 struct tblentry *cmdp;
6971#if ENABLE_ASH_ALIAS
6972 const struct alias *ap;
6973#endif
6974 const char *path = pathval();
6975
6976 if (describe_command_verbose) {
6977 out1str(command);
6978 }
6979
6980 /* First look at the keywords */
6981 if (findkwd(command)) {
6982 out1str(describe_command_verbose ? " is a shell keyword" : command);
6983 goto out;
6984 }
6985
6986#if ENABLE_ASH_ALIAS
6987 /* Then look at the aliases */
6988 ap = lookupalias(command, 0);
6989 if (ap != NULL) {
6990 if (describe_command_verbose) {
6991 out1fmt(" is an alias for %s", ap->val);
6992 } else {
6993 out1str("alias ");
6994 printalias(ap);
6995 return 0;
6996 }
6997 goto out;
6998 }
6999#endif
7000 /* Then check if it is a tracked alias */
7001 cmdp = cmdlookup(command, 0);
7002 if (cmdp != NULL) {
7003 entry.cmdtype = cmdp->cmdtype;
7004 entry.u = cmdp->param;
7005 } else {
7006 /* Finally use brute force */
7007 find_command(command, &entry, DO_ABS, path);
7008 }
7009
7010 switch (entry.cmdtype) {
7011 case CMDNORMAL: {
7012 int j = entry.u.index;
7013 char *p;
7014 if (j == -1) {
7015 p = command;
7016 } else {
7017 do {
7018 p = padvance(&path, command);
7019 stunalloc(p);
7020 } while (--j >= 0);
7021 }
7022 if (describe_command_verbose) {
7023 out1fmt(" is%s %s",
7024 (cmdp ? " a tracked alias for" : nullstr), p
7025 );
7026 } else {
7027 out1str(p);
7028 }
7029 break;
7030 }
7031
7032 case CMDFUNCTION:
7033 if (describe_command_verbose) {
7034 out1str(" is a shell function");
7035 } else {
7036 out1str(command);
7037 }
7038 break;
7039
7040 case CMDBUILTIN:
7041 if (describe_command_verbose) {
7042 out1fmt(" is a %sshell builtin",
7043 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7044 "special " : nullstr
7045 );
7046 } else {
7047 out1str(command);
7048 }
7049 break;
7050
7051 default:
7052 if (describe_command_verbose) {
7053 out1str(": not found\n");
7054 }
7055 return 127;
7056 }
7057 out:
7058 outstr("\n", stdout);
7059 return 0;
7060}
7061
7062static int
7063typecmd(int argc, char **argv)
7064{
7065 int i;
7066 int err = 0;
7067
7068 for (i = 1; i < argc; i++) {
7069#if ENABLE_ASH_CMDCMD
7070 err |= describe_command(argv[i], 1);
7071#else
7072 err |= describe_command(argv[i]);
7073#endif
7074 }
7075 return err;
7076}
7077
7078#if ENABLE_ASH_CMDCMD
7079static int
7080commandcmd(int argc, char **argv)
7081{
7082 int c;
7083 enum {
7084 VERIFY_BRIEF = 1,
7085 VERIFY_VERBOSE = 2,
7086 } verify = 0;
7087
7088 while ((c = nextopt("pvV")) != '\0')
7089 if (c == 'V')
7090 verify |= VERIFY_VERBOSE;
7091 else if (c == 'v')
7092 verify |= VERIFY_BRIEF;
7093#if DEBUG
7094 else if (c != 'p')
7095 abort();
7096#endif
7097 if (verify)
7098 return describe_command(*argptr, verify - VERIFY_BRIEF);
7099
7100 return 0;
7101}
7102#endif
7103
7104
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007105/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007106
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007107static int funcblocksize; /* size of structures in function */
7108static int funcstringsize; /* size of strings in node */
7109static void *funcblock; /* block to allocate function from */
7110static char *funcstring; /* block to allocate strings from */
7111
Eric Andersencb57d552001-06-28 07:25:16 +00007112/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007113#define EV_EXIT 01 /* exit after evaluating tree */
7114#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7115#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007116
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007117static const short nodesize[26] = {
7118 SHELL_ALIGN(sizeof(struct ncmd)),
7119 SHELL_ALIGN(sizeof(struct npipe)),
7120 SHELL_ALIGN(sizeof(struct nredir)),
7121 SHELL_ALIGN(sizeof(struct nredir)),
7122 SHELL_ALIGN(sizeof(struct nredir)),
7123 SHELL_ALIGN(sizeof(struct nbinary)),
7124 SHELL_ALIGN(sizeof(struct nbinary)),
7125 SHELL_ALIGN(sizeof(struct nbinary)),
7126 SHELL_ALIGN(sizeof(struct nif)),
7127 SHELL_ALIGN(sizeof(struct nbinary)),
7128 SHELL_ALIGN(sizeof(struct nbinary)),
7129 SHELL_ALIGN(sizeof(struct nfor)),
7130 SHELL_ALIGN(sizeof(struct ncase)),
7131 SHELL_ALIGN(sizeof(struct nclist)),
7132 SHELL_ALIGN(sizeof(struct narg)),
7133 SHELL_ALIGN(sizeof(struct narg)),
7134 SHELL_ALIGN(sizeof(struct nfile)),
7135 SHELL_ALIGN(sizeof(struct nfile)),
7136 SHELL_ALIGN(sizeof(struct nfile)),
7137 SHELL_ALIGN(sizeof(struct nfile)),
7138 SHELL_ALIGN(sizeof(struct nfile)),
7139 SHELL_ALIGN(sizeof(struct ndup)),
7140 SHELL_ALIGN(sizeof(struct ndup)),
7141 SHELL_ALIGN(sizeof(struct nhere)),
7142 SHELL_ALIGN(sizeof(struct nhere)),
7143 SHELL_ALIGN(sizeof(struct nnot)),
7144};
7145
7146static void calcsize(union node *n);
7147
7148static void
7149sizenodelist(struct nodelist *lp)
7150{
7151 while (lp) {
7152 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7153 calcsize(lp->n);
7154 lp = lp->next;
7155 }
7156}
7157
7158static void
7159calcsize(union node *n)
7160{
7161 if (n == NULL)
7162 return;
7163 funcblocksize += nodesize[n->type];
7164 switch (n->type) {
7165 case NCMD:
7166 calcsize(n->ncmd.redirect);
7167 calcsize(n->ncmd.args);
7168 calcsize(n->ncmd.assign);
7169 break;
7170 case NPIPE:
7171 sizenodelist(n->npipe.cmdlist);
7172 break;
7173 case NREDIR:
7174 case NBACKGND:
7175 case NSUBSHELL:
7176 calcsize(n->nredir.redirect);
7177 calcsize(n->nredir.n);
7178 break;
7179 case NAND:
7180 case NOR:
7181 case NSEMI:
7182 case NWHILE:
7183 case NUNTIL:
7184 calcsize(n->nbinary.ch2);
7185 calcsize(n->nbinary.ch1);
7186 break;
7187 case NIF:
7188 calcsize(n->nif.elsepart);
7189 calcsize(n->nif.ifpart);
7190 calcsize(n->nif.test);
7191 break;
7192 case NFOR:
7193 funcstringsize += strlen(n->nfor.var) + 1;
7194 calcsize(n->nfor.body);
7195 calcsize(n->nfor.args);
7196 break;
7197 case NCASE:
7198 calcsize(n->ncase.cases);
7199 calcsize(n->ncase.expr);
7200 break;
7201 case NCLIST:
7202 calcsize(n->nclist.body);
7203 calcsize(n->nclist.pattern);
7204 calcsize(n->nclist.next);
7205 break;
7206 case NDEFUN:
7207 case NARG:
7208 sizenodelist(n->narg.backquote);
7209 funcstringsize += strlen(n->narg.text) + 1;
7210 calcsize(n->narg.next);
7211 break;
7212 case NTO:
7213 case NCLOBBER:
7214 case NFROM:
7215 case NFROMTO:
7216 case NAPPEND:
7217 calcsize(n->nfile.fname);
7218 calcsize(n->nfile.next);
7219 break;
7220 case NTOFD:
7221 case NFROMFD:
7222 calcsize(n->ndup.vname);
7223 calcsize(n->ndup.next);
7224 break;
7225 case NHERE:
7226 case NXHERE:
7227 calcsize(n->nhere.doc);
7228 calcsize(n->nhere.next);
7229 break;
7230 case NNOT:
7231 calcsize(n->nnot.com);
7232 break;
7233 };
7234}
7235
7236static char *
7237nodeckstrdup(char *s)
7238{
7239 char *rtn = funcstring;
7240
7241 strcpy(funcstring, s);
7242 funcstring += strlen(s) + 1;
7243 return rtn;
7244}
7245
7246static union node *copynode(union node *);
7247
7248static struct nodelist *
7249copynodelist(struct nodelist *lp)
7250{
7251 struct nodelist *start;
7252 struct nodelist **lpp;
7253
7254 lpp = &start;
7255 while (lp) {
7256 *lpp = funcblock;
7257 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7258 (*lpp)->n = copynode(lp->n);
7259 lp = lp->next;
7260 lpp = &(*lpp)->next;
7261 }
7262 *lpp = NULL;
7263 return start;
7264}
7265
7266static union node *
7267copynode(union node *n)
7268{
7269 union node *new;
7270
7271 if (n == NULL)
7272 return NULL;
7273 new = funcblock;
7274 funcblock = (char *) funcblock + nodesize[n->type];
7275
7276 switch (n->type) {
7277 case NCMD:
7278 new->ncmd.redirect = copynode(n->ncmd.redirect);
7279 new->ncmd.args = copynode(n->ncmd.args);
7280 new->ncmd.assign = copynode(n->ncmd.assign);
7281 break;
7282 case NPIPE:
7283 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7284 new->npipe.backgnd = n->npipe.backgnd;
7285 break;
7286 case NREDIR:
7287 case NBACKGND:
7288 case NSUBSHELL:
7289 new->nredir.redirect = copynode(n->nredir.redirect);
7290 new->nredir.n = copynode(n->nredir.n);
7291 break;
7292 case NAND:
7293 case NOR:
7294 case NSEMI:
7295 case NWHILE:
7296 case NUNTIL:
7297 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7298 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7299 break;
7300 case NIF:
7301 new->nif.elsepart = copynode(n->nif.elsepart);
7302 new->nif.ifpart = copynode(n->nif.ifpart);
7303 new->nif.test = copynode(n->nif.test);
7304 break;
7305 case NFOR:
7306 new->nfor.var = nodeckstrdup(n->nfor.var);
7307 new->nfor.body = copynode(n->nfor.body);
7308 new->nfor.args = copynode(n->nfor.args);
7309 break;
7310 case NCASE:
7311 new->ncase.cases = copynode(n->ncase.cases);
7312 new->ncase.expr = copynode(n->ncase.expr);
7313 break;
7314 case NCLIST:
7315 new->nclist.body = copynode(n->nclist.body);
7316 new->nclist.pattern = copynode(n->nclist.pattern);
7317 new->nclist.next = copynode(n->nclist.next);
7318 break;
7319 case NDEFUN:
7320 case NARG:
7321 new->narg.backquote = copynodelist(n->narg.backquote);
7322 new->narg.text = nodeckstrdup(n->narg.text);
7323 new->narg.next = copynode(n->narg.next);
7324 break;
7325 case NTO:
7326 case NCLOBBER:
7327 case NFROM:
7328 case NFROMTO:
7329 case NAPPEND:
7330 new->nfile.fname = copynode(n->nfile.fname);
7331 new->nfile.fd = n->nfile.fd;
7332 new->nfile.next = copynode(n->nfile.next);
7333 break;
7334 case NTOFD:
7335 case NFROMFD:
7336 new->ndup.vname = copynode(n->ndup.vname);
7337 new->ndup.dupfd = n->ndup.dupfd;
7338 new->ndup.fd = n->ndup.fd;
7339 new->ndup.next = copynode(n->ndup.next);
7340 break;
7341 case NHERE:
7342 case NXHERE:
7343 new->nhere.doc = copynode(n->nhere.doc);
7344 new->nhere.fd = n->nhere.fd;
7345 new->nhere.next = copynode(n->nhere.next);
7346 break;
7347 case NNOT:
7348 new->nnot.com = copynode(n->nnot.com);
7349 break;
7350 };
7351 new->type = n->type;
7352 return new;
7353}
7354
7355/*
7356 * Make a copy of a parse tree.
7357 */
7358static struct funcnode *
7359copyfunc(union node *n)
7360{
7361 struct funcnode *f;
7362 size_t blocksize;
7363
7364 funcblocksize = offsetof(struct funcnode, n);
7365 funcstringsize = 0;
7366 calcsize(n);
7367 blocksize = funcblocksize;
7368 f = ckmalloc(blocksize + funcstringsize);
7369 funcblock = (char *) f + offsetof(struct funcnode, n);
7370 funcstring = (char *) f + blocksize;
7371 copynode(n);
7372 f->count = 0;
7373 return f;
7374}
7375
7376/*
7377 * Define a shell function.
7378 */
7379static void
7380defun(char *name, union node *func)
7381{
7382 struct cmdentry entry;
7383
7384 INT_OFF;
7385 entry.cmdtype = CMDFUNCTION;
7386 entry.u.func = copyfunc(func);
7387 addcmdentry(name, &entry);
7388 INT_ON;
7389}
7390
7391static int evalskip; /* set if we are skipping commands */
7392/* reasons for skipping commands (see comment on breakcmd routine) */
7393#define SKIPBREAK (1 << 0)
7394#define SKIPCONT (1 << 1)
7395#define SKIPFUNC (1 << 2)
7396#define SKIPFILE (1 << 3)
7397#define SKIPEVAL (1 << 4)
7398static int skipcount; /* number of levels to skip */
7399static int funcnest; /* depth of function calls */
7400
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007401/* forward decl way out to parsing code - dotrap needs it */
7402static int evalstring(char *s, int mask);
7403
7404/*
7405 * Called to execute a trap. Perhaps we should avoid entering new trap
7406 * handlers while we are executing a trap handler.
7407 */
7408static int
7409dotrap(void)
7410{
7411 char *p;
7412 char *q;
7413 int i;
7414 int savestatus;
7415 int skip = 0;
7416
7417 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007418 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007419 xbarrier();
7420
7421 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7422 if (!*q)
7423 continue;
7424 *q = '\0';
7425
7426 p = trap[i + 1];
7427 if (!p)
7428 continue;
7429 skip = evalstring(p, SKIPEVAL);
7430 exitstatus = savestatus;
7431 if (skip)
7432 break;
7433 }
7434
7435 return skip;
7436}
7437
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007438/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007439static void evalloop(union node *, int);
7440static void evalfor(union node *, int);
7441static void evalcase(union node *, int);
7442static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007443static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007444static void evalpipe(union node *, int);
7445static void evalcommand(union node *, int);
7446static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007447static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007448
Eric Andersen62483552001-07-10 06:09:16 +00007449/*
Eric Andersenc470f442003-07-28 09:56:35 +00007450 * Evaluate a parse tree. The value is left in the global variable
7451 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007452 */
Eric Andersenc470f442003-07-28 09:56:35 +00007453static void
7454evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007455{
Eric Andersenc470f442003-07-28 09:56:35 +00007456 int checkexit = 0;
7457 void (*evalfn)(union node *, int);
7458 unsigned isor;
7459 int status;
7460 if (n == NULL) {
7461 TRACE(("evaltree(NULL) called\n"));
7462 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007463 }
Eric Andersenc470f442003-07-28 09:56:35 +00007464 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007465 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007466 switch (n->type) {
7467 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007468#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007469 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007470 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007471 break;
7472#endif
7473 case NNOT:
7474 evaltree(n->nnot.com, EV_TESTED);
7475 status = !exitstatus;
7476 goto setstatus;
7477 case NREDIR:
7478 expredir(n->nredir.redirect);
7479 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7480 if (!status) {
7481 evaltree(n->nredir.n, flags & EV_TESTED);
7482 status = exitstatus;
7483 }
7484 popredir(0);
7485 goto setstatus;
7486 case NCMD:
7487 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007488 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007489 if (eflag && !(flags & EV_TESTED))
7490 checkexit = ~0;
7491 goto calleval;
7492 case NFOR:
7493 evalfn = evalfor;
7494 goto calleval;
7495 case NWHILE:
7496 case NUNTIL:
7497 evalfn = evalloop;
7498 goto calleval;
7499 case NSUBSHELL:
7500 case NBACKGND:
7501 evalfn = evalsubshell;
7502 goto calleval;
7503 case NPIPE:
7504 evalfn = evalpipe;
7505 goto checkexit;
7506 case NCASE:
7507 evalfn = evalcase;
7508 goto calleval;
7509 case NAND:
7510 case NOR:
7511 case NSEMI:
7512#if NAND + 1 != NOR
7513#error NAND + 1 != NOR
7514#endif
7515#if NOR + 1 != NSEMI
7516#error NOR + 1 != NSEMI
7517#endif
7518 isor = n->type - NAND;
7519 evaltree(
7520 n->nbinary.ch1,
7521 (flags | ((isor >> 1) - 1)) & EV_TESTED
7522 );
7523 if (!exitstatus == isor)
7524 break;
7525 if (!evalskip) {
7526 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007527 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007528 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007529 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007530 evalfn(n, flags);
7531 break;
7532 }
7533 break;
7534 case NIF:
7535 evaltree(n->nif.test, EV_TESTED);
7536 if (evalskip)
7537 break;
7538 if (exitstatus == 0) {
7539 n = n->nif.ifpart;
7540 goto evaln;
7541 } else if (n->nif.elsepart) {
7542 n = n->nif.elsepart;
7543 goto evaln;
7544 }
7545 goto success;
7546 case NDEFUN:
7547 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007548 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007549 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007550 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007551 exitstatus = status;
7552 break;
7553 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007554 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007555 if ((checkexit & exitstatus))
7556 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007557 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007558 goto exexit;
7559
7560 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007561 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007562 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007563 }
Eric Andersen62483552001-07-10 06:09:16 +00007564}
7565
Eric Andersenc470f442003-07-28 09:56:35 +00007566#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7567static
7568#endif
7569void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7570
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007571static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007572
7573static void
7574evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007575{
7576 int status;
7577
7578 loopnest++;
7579 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007580 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007581 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007582 int i;
7583
Eric Andersencb57d552001-06-28 07:25:16 +00007584 evaltree(n->nbinary.ch1, EV_TESTED);
7585 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007586 skipping:
7587 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007588 evalskip = 0;
7589 continue;
7590 }
7591 if (evalskip == SKIPBREAK && --skipcount <= 0)
7592 evalskip = 0;
7593 break;
7594 }
Eric Andersenc470f442003-07-28 09:56:35 +00007595 i = exitstatus;
7596 if (n->type != NWHILE)
7597 i = !i;
7598 if (i != 0)
7599 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007600 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007601 status = exitstatus;
7602 if (evalskip)
7603 goto skipping;
7604 }
7605 loopnest--;
7606 exitstatus = status;
7607}
7608
Eric Andersenc470f442003-07-28 09:56:35 +00007609static void
7610evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007611{
7612 struct arglist arglist;
7613 union node *argp;
7614 struct strlist *sp;
7615 struct stackmark smark;
7616
7617 setstackmark(&smark);
7618 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007619 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007620 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007621 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007622 if (evalskip)
7623 goto out;
7624 }
7625 *arglist.lastp = NULL;
7626
7627 exitstatus = 0;
7628 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007629 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007630 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007631 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007632 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007633 if (evalskip) {
7634 if (evalskip == SKIPCONT && --skipcount <= 0) {
7635 evalskip = 0;
7636 continue;
7637 }
7638 if (evalskip == SKIPBREAK && --skipcount <= 0)
7639 evalskip = 0;
7640 break;
7641 }
7642 }
7643 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007644 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007645 popstackmark(&smark);
7646}
7647
Eric Andersenc470f442003-07-28 09:56:35 +00007648static void
7649evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007650{
7651 union node *cp;
7652 union node *patp;
7653 struct arglist arglist;
7654 struct stackmark smark;
7655
7656 setstackmark(&smark);
7657 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007658 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007659 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007660 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7661 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007662 if (casematch(patp, arglist.list->text)) {
7663 if (evalskip == 0) {
7664 evaltree(cp->nclist.body, flags);
7665 }
7666 goto out;
7667 }
7668 }
7669 }
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 +00007674/*
7675 * Kick off a subshell to evaluate a tree.
7676 */
Eric Andersenc470f442003-07-28 09:56:35 +00007677static void
7678evalsubshell(union node *n, int flags)
7679{
7680 struct job *jp;
7681 int backgnd = (n->type == NBACKGND);
7682 int status;
7683
7684 expredir(n->nredir.redirect);
7685 if (!backgnd && flags & EV_EXIT && !trap[0])
7686 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007687 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007688 jp = makejob(n, 1);
7689 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007690 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007691 flags |= EV_EXIT;
7692 if (backgnd)
7693 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007694 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007695 redirect(n->nredir.redirect, 0);
7696 evaltreenr(n->nredir.n, flags);
7697 /* never returns */
7698 }
7699 status = 0;
7700 if (! backgnd)
7701 status = waitforjob(jp);
7702 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007703 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007704}
7705
Eric Andersenc470f442003-07-28 09:56:35 +00007706/*
7707 * Compute the names of the files in a redirection list.
7708 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007709static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007710static void
7711expredir(union node *n)
7712{
7713 union node *redir;
7714
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007715 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007716 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007717
7718 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007719 fn.lastp = &fn.list;
7720 switch (redir->type) {
7721 case NFROMTO:
7722 case NFROM:
7723 case NTO:
7724 case NCLOBBER:
7725 case NAPPEND:
7726 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7727 redir->nfile.expfname = fn.list->text;
7728 break;
7729 case NFROMFD:
7730 case NTOFD:
7731 if (redir->ndup.vname) {
7732 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007733 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007734 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007735 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007736 }
7737 break;
7738 }
7739 }
7740}
7741
Eric Andersencb57d552001-06-28 07:25:16 +00007742/*
Eric Andersencb57d552001-06-28 07:25:16 +00007743 * Evaluate a pipeline. All the processes in the pipeline are children
7744 * of the process creating the pipeline. (This differs from some versions
7745 * of the shell, which make the last process in a pipeline the parent
7746 * of all the rest.)
7747 */
Eric Andersenc470f442003-07-28 09:56:35 +00007748static void
7749evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007750{
7751 struct job *jp;
7752 struct nodelist *lp;
7753 int pipelen;
7754 int prevfd;
7755 int pip[2];
7756
Eric Andersenc470f442003-07-28 09:56:35 +00007757 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007758 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007759 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007760 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007761 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007762 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007763 jp = makejob(n, pipelen);
7764 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007765 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007766 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007767 pip[1] = -1;
7768 if (lp->next) {
7769 if (pipe(pip) < 0) {
7770 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007771 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007772 }
7773 }
7774 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007775 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007776 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007777 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007778 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007779 if (prevfd > 0) {
7780 dup2(prevfd, 0);
7781 close(prevfd);
7782 }
7783 if (pip[1] > 1) {
7784 dup2(pip[1], 1);
7785 close(pip[1]);
7786 }
Eric Andersenc470f442003-07-28 09:56:35 +00007787 evaltreenr(lp->n, flags);
7788 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007789 }
7790 if (prevfd >= 0)
7791 close(prevfd);
7792 prevfd = pip[0];
7793 close(pip[1]);
7794 }
Eric Andersencb57d552001-06-28 07:25:16 +00007795 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007796 exitstatus = waitforjob(jp);
7797 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007798 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007799 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007800}
7801
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007802/*
7803 * Controls whether the shell is interactive or not.
7804 */
7805static void
7806setinteractive(int on)
7807{
7808 static int is_interactive;
7809
7810 if (++on == is_interactive)
7811 return;
7812 is_interactive = on;
7813 setsignal(SIGINT);
7814 setsignal(SIGQUIT);
7815 setsignal(SIGTERM);
7816#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7817 if (is_interactive > 1) {
7818 /* Looks like they want an interactive shell */
7819 static smallint do_banner;
7820
7821 if (!do_banner) {
7822 out1fmt(
7823 "\n\n"
7824 "%s Built-in shell (ash)\n"
7825 "Enter 'help' for a list of built-in commands."
7826 "\n\n",
7827 BB_BANNER);
7828 do_banner = 1;
7829 }
7830 }
7831#endif
7832}
7833
7834#if ENABLE_FEATURE_EDITING_VI
7835#define setvimode(on) do { \
7836 if (on) line_input_state->flags |= VI_MODE; \
7837 else line_input_state->flags &= ~VI_MODE; \
7838} while (0)
7839#else
7840#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7841#endif
7842
7843static void
7844optschanged(void)
7845{
7846#if DEBUG
7847 opentrace();
7848#endif
7849 setinteractive(iflag);
7850 setjobctl(mflag);
7851 setvimode(viflag);
7852}
7853
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007854static struct localvar *localvars;
7855
7856/*
7857 * Called after a function returns.
7858 * Interrupts must be off.
7859 */
7860static void
7861poplocalvars(void)
7862{
7863 struct localvar *lvp;
7864 struct var *vp;
7865
7866 while ((lvp = localvars) != NULL) {
7867 localvars = lvp->next;
7868 vp = lvp->vp;
7869 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7870 if (vp == NULL) { /* $- saved */
7871 memcpy(optlist, lvp->text, sizeof(optlist));
7872 free((char*)lvp->text);
7873 optschanged();
7874 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7875 unsetvar(vp->text);
7876 } else {
7877 if (vp->func)
7878 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7879 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7880 free((char*)vp->text);
7881 vp->flags = lvp->flags;
7882 vp->text = lvp->text;
7883 }
7884 free(lvp);
7885 }
7886}
7887
7888static int
7889evalfun(struct funcnode *func, int argc, char **argv, int flags)
7890{
7891 volatile struct shparam saveparam;
7892 struct localvar *volatile savelocalvars;
7893 struct jmploc *volatile savehandler;
7894 struct jmploc jmploc;
7895 int e;
7896
7897 saveparam = shellparam;
7898 savelocalvars = localvars;
7899 e = setjmp(jmploc.loc);
7900 if (e) {
7901 goto funcdone;
7902 }
7903 INT_OFF;
7904 savehandler = exception_handler;
7905 exception_handler = &jmploc;
7906 localvars = NULL;
7907 shellparam.malloc = 0;
7908 func->count++;
7909 funcnest++;
7910 INT_ON;
7911 shellparam.nparam = argc - 1;
7912 shellparam.p = argv + 1;
7913#if ENABLE_ASH_GETOPTS
7914 shellparam.optind = 1;
7915 shellparam.optoff = -1;
7916#endif
7917 evaltree(&func->n, flags & EV_TESTED);
7918funcdone:
7919 INT_OFF;
7920 funcnest--;
7921 freefunc(func);
7922 poplocalvars();
7923 localvars = savelocalvars;
7924 freeparam(&shellparam);
7925 shellparam = saveparam;
7926 exception_handler = savehandler;
7927 INT_ON;
7928 evalskip &= ~SKIPFUNC;
7929 return e;
7930}
7931
Denis Vlasenko131ae172007-02-18 13:00:19 +00007932#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007933static char **
7934parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007935{
7936 char *cp, c;
7937
7938 for (;;) {
7939 cp = *++argv;
7940 if (!cp)
7941 return 0;
7942 if (*cp++ != '-')
7943 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007944 c = *cp++;
7945 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007946 break;
7947 if (c == '-' && !*cp) {
7948 argv++;
7949 break;
7950 }
7951 do {
7952 switch (c) {
7953 case 'p':
7954 *path = defpath;
7955 break;
7956 default:
7957 /* run 'typecmd' for other options */
7958 return 0;
7959 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007960 c = *cp++;
7961 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007962 }
7963 return argv;
7964}
7965#endif
7966
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007967/*
7968 * Make a variable a local variable. When a variable is made local, it's
7969 * value and flags are saved in a localvar structure. The saved values
7970 * will be restored when the shell function returns. We handle the name
7971 * "-" as a special case.
7972 */
7973static void
7974mklocal(char *name)
7975{
7976 struct localvar *lvp;
7977 struct var **vpp;
7978 struct var *vp;
7979
7980 INT_OFF;
7981 lvp = ckmalloc(sizeof(struct localvar));
7982 if (LONE_DASH(name)) {
7983 char *p;
7984 p = ckmalloc(sizeof(optlist));
7985 lvp->text = memcpy(p, optlist, sizeof(optlist));
7986 vp = NULL;
7987 } else {
7988 char *eq;
7989
7990 vpp = hashvar(name);
7991 vp = *findvar(vpp, name);
7992 eq = strchr(name, '=');
7993 if (vp == NULL) {
7994 if (eq)
7995 setvareq(name, VSTRFIXED);
7996 else
7997 setvar(name, NULL, VSTRFIXED);
7998 vp = *vpp; /* the new variable */
7999 lvp->flags = VUNSET;
8000 } else {
8001 lvp->text = vp->text;
8002 lvp->flags = vp->flags;
8003 vp->flags |= VSTRFIXED|VTEXTFIXED;
8004 if (eq)
8005 setvareq(name, 0);
8006 }
8007 }
8008 lvp->vp = vp;
8009 lvp->next = localvars;
8010 localvars = lvp;
8011 INT_ON;
8012}
8013
8014/*
8015 * The "local" command.
8016 */
8017static int
8018localcmd(int argc, char **argv)
8019{
8020 char *name;
8021
8022 argv = argptr;
8023 while ((name = *argv++) != NULL) {
8024 mklocal(name);
8025 }
8026 return 0;
8027}
8028
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008029static int
8030falsecmd(int argc, char **argv)
8031{
8032 return 1;
8033}
8034
8035static int
8036truecmd(int argc, char **argv)
8037{
8038 return 0;
8039}
8040
8041static int
8042execcmd(int argc, char **argv)
8043{
8044 if (argc > 1) {
8045 iflag = 0; /* exit on error */
8046 mflag = 0;
8047 optschanged();
8048 shellexec(argv + 1, pathval(), 0);
8049 }
8050 return 0;
8051}
8052
8053/*
8054 * The return command.
8055 */
8056static int
8057returncmd(int argc, char **argv)
8058{
8059 /*
8060 * If called outside a function, do what ksh does;
8061 * skip the rest of the file.
8062 */
8063 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8064 return argv[1] ? number(argv[1]) : exitstatus;
8065}
8066
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008067/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008068static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008069static int dotcmd(int, char **);
8070static int evalcmd(int, char **);
8071#if ENABLE_ASH_BUILTIN_ECHO
8072static int echocmd(int, char **);
8073#endif
8074#if ENABLE_ASH_BUILTIN_TEST
8075static int testcmd(int, char **);
8076#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008077static int exitcmd(int, char **);
8078static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008079#if ENABLE_ASH_GETOPTS
8080static int getoptscmd(int, char **);
8081#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008082#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8083static int helpcmd(int argc, char **argv);
8084#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008085#if ENABLE_ASH_MATH_SUPPORT
8086static int letcmd(int, char **);
8087#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008088static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008089static int setcmd(int, char **);
8090static int shiftcmd(int, char **);
8091static int timescmd(int, char **);
8092static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008093static int umaskcmd(int, char **);
8094static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008095static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008096
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008097#define BUILTIN_NOSPEC "0"
8098#define BUILTIN_SPECIAL "1"
8099#define BUILTIN_REGULAR "2"
8100#define BUILTIN_SPEC_REG "3"
8101#define BUILTIN_ASSIGN "4"
8102#define BUILTIN_SPEC_ASSG "5"
8103#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008104#define BUILTIN_SPEC_REG_ASSG "7"
8105
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008106/* make sure to keep these in proper order since it is searched via bsearch() */
8107static const struct builtincmd builtintab[] = {
8108 { BUILTIN_SPEC_REG ".", dotcmd },
8109 { BUILTIN_SPEC_REG ":", truecmd },
8110#if ENABLE_ASH_BUILTIN_TEST
8111 { BUILTIN_REGULAR "[", testcmd },
8112 { BUILTIN_REGULAR "[[", testcmd },
8113#endif
8114#if ENABLE_ASH_ALIAS
8115 { BUILTIN_REG_ASSG "alias", aliascmd },
8116#endif
8117#if JOBS
8118 { BUILTIN_REGULAR "bg", fg_bgcmd },
8119#endif
8120 { BUILTIN_SPEC_REG "break", breakcmd },
8121 { BUILTIN_REGULAR "cd", cdcmd },
8122 { BUILTIN_NOSPEC "chdir", cdcmd },
8123#if ENABLE_ASH_CMDCMD
8124 { BUILTIN_REGULAR "command", commandcmd },
8125#endif
8126 { BUILTIN_SPEC_REG "continue", breakcmd },
8127#if ENABLE_ASH_BUILTIN_ECHO
8128 { BUILTIN_REGULAR "echo", echocmd },
8129#endif
8130 { BUILTIN_SPEC_REG "eval", evalcmd },
8131 { BUILTIN_SPEC_REG "exec", execcmd },
8132 { BUILTIN_SPEC_REG "exit", exitcmd },
8133 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8134 { BUILTIN_REGULAR "false", falsecmd },
8135#if JOBS
8136 { BUILTIN_REGULAR "fg", fg_bgcmd },
8137#endif
8138#if ENABLE_ASH_GETOPTS
8139 { BUILTIN_REGULAR "getopts", getoptscmd },
8140#endif
8141 { BUILTIN_NOSPEC "hash", hashcmd },
8142#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8143 { BUILTIN_NOSPEC "help", helpcmd },
8144#endif
8145#if JOBS
8146 { BUILTIN_REGULAR "jobs", jobscmd },
8147 { BUILTIN_REGULAR "kill", killcmd },
8148#endif
8149#if ENABLE_ASH_MATH_SUPPORT
8150 { BUILTIN_NOSPEC "let", letcmd },
8151#endif
8152 { BUILTIN_ASSIGN "local", localcmd },
8153 { BUILTIN_NOSPEC "pwd", pwdcmd },
8154 { BUILTIN_REGULAR "read", readcmd },
8155 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8156 { BUILTIN_SPEC_REG "return", returncmd },
8157 { BUILTIN_SPEC_REG "set", setcmd },
8158 { BUILTIN_SPEC_REG "shift", shiftcmd },
8159 { BUILTIN_SPEC_REG "source", dotcmd },
8160#if ENABLE_ASH_BUILTIN_TEST
8161 { BUILTIN_REGULAR "test", testcmd },
8162#endif
8163 { BUILTIN_SPEC_REG "times", timescmd },
8164 { BUILTIN_SPEC_REG "trap", trapcmd },
8165 { BUILTIN_REGULAR "true", truecmd },
8166 { BUILTIN_NOSPEC "type", typecmd },
8167 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8168 { BUILTIN_REGULAR "umask", umaskcmd },
8169#if ENABLE_ASH_ALIAS
8170 { BUILTIN_REGULAR "unalias", unaliascmd },
8171#endif
8172 { BUILTIN_SPEC_REG "unset", unsetcmd },
8173 { BUILTIN_REGULAR "wait", waitcmd },
8174};
8175
8176#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
8177
8178#define COMMANDCMD (builtintab + 5 + \
8179 2 * ENABLE_ASH_BUILTIN_TEST + \
8180 ENABLE_ASH_ALIAS + \
8181 ENABLE_ASH_JOB_CONTROL)
8182#define EXECCMD (builtintab + 7 + \
8183 2 * ENABLE_ASH_BUILTIN_TEST + \
8184 ENABLE_ASH_ALIAS + \
8185 ENABLE_ASH_JOB_CONTROL + \
8186 ENABLE_ASH_CMDCMD + \
8187 ENABLE_ASH_BUILTIN_ECHO)
8188
8189/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008190 * Search the table of builtin commands.
8191 */
8192static struct builtincmd *
8193find_builtin(const char *name)
8194{
8195 struct builtincmd *bp;
8196
8197 bp = bsearch(
8198 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
8199 pstrcmp
8200 );
8201 return bp;
8202}
8203
8204/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008205 * Execute a simple command.
8206 */
8207static int back_exitstatus; /* exit status of backquoted command */
8208static int
8209isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008210{
8211 const char *q = endofname(p);
8212 if (p == q)
8213 return 0;
8214 return *q == '=';
8215}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008216static int
8217bltincmd(int argc, char **argv)
8218{
8219 /* Preserve exitstatus of a previous possible redirection
8220 * as POSIX mandates */
8221 return back_exitstatus;
8222}
Eric Andersenc470f442003-07-28 09:56:35 +00008223static void
8224evalcommand(union node *cmd, int flags)
8225{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008226 static const struct builtincmd bltin = {
8227 "\0\0", bltincmd
8228 };
Eric Andersenc470f442003-07-28 09:56:35 +00008229 struct stackmark smark;
8230 union node *argp;
8231 struct arglist arglist;
8232 struct arglist varlist;
8233 char **argv;
8234 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008235 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008236 struct cmdentry cmdentry;
8237 struct job *jp;
8238 char *lastarg;
8239 const char *path;
8240 int spclbltin;
8241 int cmd_is_exec;
8242 int status;
8243 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008244 struct builtincmd *bcmd;
8245 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008246
8247 /* First expand the arguments. */
8248 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8249 setstackmark(&smark);
8250 back_exitstatus = 0;
8251
8252 cmdentry.cmdtype = CMDBUILTIN;
8253 cmdentry.u.cmd = &bltin;
8254 varlist.lastp = &varlist.list;
8255 *varlist.lastp = NULL;
8256 arglist.lastp = &arglist.list;
8257 *arglist.lastp = NULL;
8258
8259 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008260 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008261 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8262 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8263 }
8264
Eric Andersenc470f442003-07-28 09:56:35 +00008265 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8266 struct strlist **spp;
8267
8268 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008269 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008270 expandarg(argp, &arglist, EXP_VARTILDE);
8271 else
8272 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8273
Eric Andersenc470f442003-07-28 09:56:35 +00008274 for (sp = *spp; sp; sp = sp->next)
8275 argc++;
8276 }
8277
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008278 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008279 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008280 TRACE(("evalcommand arg: %s\n", sp->text));
8281 *nargv++ = sp->text;
8282 }
8283 *nargv = NULL;
8284
8285 lastarg = NULL;
8286 if (iflag && funcnest == 0 && argc > 0)
8287 lastarg = nargv[-1];
8288
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008289 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008290 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008291 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008292
8293 path = vpath.text;
8294 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8295 struct strlist **spp;
8296 char *p;
8297
8298 spp = varlist.lastp;
8299 expandarg(argp, &varlist, EXP_VARTILDE);
8300
8301 /*
8302 * Modify the command lookup path, if a PATH= assignment
8303 * is present
8304 */
8305 p = (*spp)->text;
8306 if (varequal(p, path))
8307 path = p;
8308 }
8309
8310 /* Print the command if xflag is set. */
8311 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008312 int n;
8313 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008314
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008315 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008316 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008317
8318 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008319 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008320 while (sp) {
8321 dprintf(preverrout_fd, p, sp->text);
8322 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008323 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008324 p--;
8325 }
8326 }
8327 sp = arglist.list;
8328 }
Rob Landley53437472006-07-16 08:14:35 +00008329 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008330 }
8331
8332 cmd_is_exec = 0;
8333 spclbltin = -1;
8334
8335 /* Now locate the command. */
8336 if (argc) {
8337 const char *oldpath;
8338 int cmd_flag = DO_ERR;
8339
8340 path += 5;
8341 oldpath = path;
8342 for (;;) {
8343 find_command(argv[0], &cmdentry, cmd_flag, path);
8344 if (cmdentry.cmdtype == CMDUNKNOWN) {
8345 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008346 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008347 goto bail;
8348 }
8349
8350 /* implement bltin and command here */
8351 if (cmdentry.cmdtype != CMDBUILTIN)
8352 break;
8353 if (spclbltin < 0)
8354 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8355 if (cmdentry.u.cmd == EXECCMD)
8356 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008357#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008358 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008359 path = oldpath;
8360 nargv = parse_command_args(argv, &path);
8361 if (!nargv)
8362 break;
8363 argc -= nargv - argv;
8364 argv = nargv;
8365 cmd_flag |= DO_NOFUNC;
8366 } else
8367#endif
8368 break;
8369 }
8370 }
8371
8372 if (status) {
8373 /* We have a redirection error. */
8374 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008375 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008376 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008377 exitstatus = status;
8378 goto out;
8379 }
8380
8381 /* Execute the command. */
8382 switch (cmdentry.cmdtype) {
8383 default:
8384 /* Fork off a child process if necessary. */
8385 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008386 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008387 jp = makejob(cmd, 1);
8388 if (forkshell(jp, cmd, FORK_FG) != 0) {
8389 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008390 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008391 break;
8392 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008393 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008394 }
8395 listsetvar(varlist.list, VEXPORT|VSTACK);
8396 shellexec(argv, path, cmdentry.u.index);
8397 /* NOTREACHED */
8398
8399 case CMDBUILTIN:
8400 cmdenviron = varlist.list;
8401 if (cmdenviron) {
8402 struct strlist *list = cmdenviron;
8403 int i = VNOSET;
8404 if (spclbltin > 0 || argc == 0) {
8405 i = 0;
8406 if (cmd_is_exec && argc > 1)
8407 i = VEXPORT;
8408 }
8409 listsetvar(list, i);
8410 }
8411 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8412 int exit_status;
8413 int i, j;
8414
8415 i = exception;
8416 if (i == EXEXIT)
8417 goto raise;
8418
8419 exit_status = 2;
8420 j = 0;
8421 if (i == EXINT)
8422 j = SIGINT;
8423 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008424 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008425 if (j)
8426 exit_status = j + 128;
8427 exitstatus = exit_status;
8428
8429 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008430 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008431 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008432 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008433 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008434 }
8435 break;
8436
8437 case CMDFUNCTION:
8438 listsetvar(varlist.list, 0);
8439 if (evalfun(cmdentry.u.func, argc, argv, flags))
8440 goto raise;
8441 break;
8442 }
8443
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008444 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008445 popredir(cmd_is_exec);
8446 if (lastarg)
8447 /* dsl: I think this is intended to be used to support
8448 * '_' in 'vi' command mode during line editing...
8449 * However I implemented that within libedit itself.
8450 */
8451 setvar("_", lastarg, 0);
8452 popstackmark(&smark);
8453}
8454
8455static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008456evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8457{
Eric Andersenc470f442003-07-28 09:56:35 +00008458 char *volatile savecmdname;
8459 struct jmploc *volatile savehandler;
8460 struct jmploc jmploc;
8461 int i;
8462
8463 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008464 i = setjmp(jmploc.loc);
8465 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008466 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008467 savehandler = exception_handler;
8468 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008469 commandname = argv[0];
8470 argptr = argv + 1;
8471 optptr = NULL; /* initialize nextopt */
8472 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008473 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008474 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008475 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008476 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008477 commandname = savecmdname;
8478 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008479 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008480
8481 return i;
8482}
8483
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008484static int
8485goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008486{
8487 return !*endofname(p);
8488}
8489
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008490
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008491/*
8492 * Search for a command. This is called before we fork so that the
8493 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008494 * the child. The check for "goodname" is an overly conservative
8495 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008496 */
Eric Andersenc470f442003-07-28 09:56:35 +00008497static void
8498prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008499{
8500 struct cmdentry entry;
8501
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008502 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8503 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008504}
8505
Eric Andersencb57d552001-06-28 07:25:16 +00008506
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008507/* ============ Builtin commands
8508 *
8509 * Builtin commands whose functions are closely tied to evaluation
8510 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008511 */
8512
8513/*
Eric Andersencb57d552001-06-28 07:25:16 +00008514 * Handle break and continue commands. Break, continue, and return are
8515 * all handled by setting the evalskip flag. The evaluation routines
8516 * above all check this flag, and if it is set they start skipping
8517 * commands rather than executing them. The variable skipcount is
8518 * the number of loops to break/continue, or the number of function
8519 * levels to return. (The latter is always 1.) It should probably
8520 * be an error to break out of more loops than exist, but it isn't
8521 * in the standard shell so we don't make it one here.
8522 */
Eric Andersenc470f442003-07-28 09:56:35 +00008523static int
8524breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008525{
8526 int n = argc > 1 ? number(argv[1]) : 1;
8527
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008528 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008529 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008530 if (n > loopnest)
8531 n = loopnest;
8532 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008533 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008534 skipcount = n;
8535 }
8536 return 0;
8537}
8538
Eric Andersenc470f442003-07-28 09:56:35 +00008539
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008540/* ============ input.c
8541 *
Eric Andersen90898442003-08-06 11:20:52 +00008542 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008543 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008544
Eric Andersenc470f442003-07-28 09:56:35 +00008545#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008546
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008547enum {
8548 INPUT_PUSH_FILE = 1,
8549 INPUT_NOFILE_OK = 2,
8550};
Eric Andersencb57d552001-06-28 07:25:16 +00008551
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008552/*
8553 * NEOF is returned by parsecmd when it encounters an end of file. It
8554 * must be distinct from NULL, so we use the address of a variable that
8555 * happens to be handy.
8556 */
8557static int plinno = 1; /* input line number */
8558/* number of characters left in input buffer */
8559static int parsenleft; /* copy of parsefile->nleft */
8560static int parselleft; /* copy of parsefile->lleft */
8561/* next character in input buffer */
8562static char *parsenextc; /* copy of parsefile->nextc */
8563
8564static int checkkwd;
8565/* values of checkkwd variable */
8566#define CHKALIAS 0x1
8567#define CHKKWD 0x2
8568#define CHKNL 0x4
8569
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008570static void
8571popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008572{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008573 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008574
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008575 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008576#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008577 if (sp->ap) {
8578 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8579 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008580 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008581 if (sp->string != sp->ap->val) {
8582 free(sp->string);
8583 }
8584 sp->ap->flag &= ~ALIASINUSE;
8585 if (sp->ap->flag & ALIASDEAD) {
8586 unalias(sp->ap->name);
8587 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008588 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008589#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008590 parsenextc = sp->prevstring;
8591 parsenleft = sp->prevnleft;
8592/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8593 parsefile->strpush = sp->prev;
8594 if (sp != &(parsefile->basestrpush))
8595 free(sp);
8596 INT_ON;
8597}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008598
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008599static int
8600preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008601{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008602 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008603 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008604 parsenextc = buf;
8605
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008606 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008607#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008608 if (!iflag || parsefile->fd)
8609 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8610 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008611#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008612 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008613#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008614 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8615 if (nr == 0) {
8616 /* Ctrl+C pressed */
8617 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008618 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008619 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008620 raise(SIGINT);
8621 return 1;
8622 }
Eric Andersenc470f442003-07-28 09:56:35 +00008623 goto retry;
8624 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008625 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008626 /* Ctrl+D presend */
8627 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008628 }
Eric Andersencb57d552001-06-28 07:25:16 +00008629 }
8630#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008631 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008632#endif
8633
8634 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008635 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
8636 int flags = fcntl(0, F_GETFL, 0);
8637 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008638 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008639 if (fcntl(0, F_SETFL, flags) >= 0) {
8640 out2str("sh: turning off NDELAY mode\n");
8641 goto retry;
8642 }
8643 }
8644 }
8645 }
8646 return nr;
8647}
8648
8649/*
8650 * Refill the input buffer and return the next input character:
8651 *
8652 * 1) If a string was pushed back on the input, pop it;
8653 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8654 * from a string so we can't refill the buffer, return EOF.
8655 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8656 * 4) Process input up to the next newline, deleting nul characters.
8657 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008658static int
Eric Andersenc470f442003-07-28 09:56:35 +00008659preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008660{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008661 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008662 int more;
8663 char savec;
8664
8665 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008666#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008667 if (parsenleft == -1 && parsefile->strpush->ap &&
8668 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008669 return PEOA;
8670 }
Eric Andersen2870d962001-07-02 17:27:21 +00008671#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008672 popstring();
8673 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008674 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008675 }
8676 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8677 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008678 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008679
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008680 more = parselleft;
8681 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008682 again:
8683 more = preadfd();
8684 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008685 parselleft = parsenleft = EOF_NLEFT;
8686 return PEOF;
8687 }
8688 }
8689
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008690 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008691
8692 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008693 for (;;) {
8694 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008695
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008696 more--;
8697 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008698
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008699 if (!c)
8700 memmove(q, q + 1, more);
8701 else {
8702 q++;
8703 if (c == '\n') {
8704 parsenleft = q - parsenextc - 1;
8705 break;
8706 }
Eric Andersencb57d552001-06-28 07:25:16 +00008707 }
8708
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008709 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008710 parsenleft = q - parsenextc - 1;
8711 if (parsenleft < 0)
8712 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008713 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008714 }
8715 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008716 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008717
8718 savec = *q;
8719 *q = '\0';
8720
8721 if (vflag) {
8722 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008723 }
8724
8725 *q = savec;
8726
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008727 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008728}
8729
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008730#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008731static int
8732pgetc(void)
8733{
8734 return pgetc_as_macro();
8735}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008736
8737#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8738#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008739#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008740#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008741#endif
8742
8743/*
8744 * Same as pgetc(), but ignores PEOA.
8745 */
8746#if ENABLE_ASH_ALIAS
8747static int
8748pgetc2(void)
8749{
8750 int c;
8751
8752 do {
8753 c = pgetc_macro();
8754 } while (c == PEOA);
8755 return c;
8756}
8757#else
8758static int
8759pgetc2(void)
8760{
8761 return pgetc_macro();
8762}
8763#endif
8764
8765/*
8766 * Read a line from the script.
8767 */
8768static char *
8769pfgets(char *line, int len)
8770{
8771 char *p = line;
8772 int nleft = len;
8773 int c;
8774
8775 while (--nleft > 0) {
8776 c = pgetc2();
8777 if (c == PEOF) {
8778 if (p == line)
8779 return NULL;
8780 break;
8781 }
8782 *p++ = c;
8783 if (c == '\n')
8784 break;
8785 }
8786 *p = '\0';
8787 return line;
8788}
8789
Eric Andersenc470f442003-07-28 09:56:35 +00008790/*
8791 * Undo the last call to pgetc. Only one character may be pushed back.
8792 * PEOF may be pushed back.
8793 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008794static void
Eric Andersenc470f442003-07-28 09:56:35 +00008795pungetc(void)
8796{
8797 parsenleft++;
8798 parsenextc--;
8799}
Eric Andersencb57d552001-06-28 07:25:16 +00008800
8801/*
8802 * Push a string back onto the input at this current parsefile level.
8803 * We handle aliases this way.
8804 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008805static void
Eric Andersenc470f442003-07-28 09:56:35 +00008806pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008807{
Eric Andersencb57d552001-06-28 07:25:16 +00008808 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008809 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008810
Eric Andersenc470f442003-07-28 09:56:35 +00008811 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008812 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008813/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8814 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008815 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008816 sp->prev = parsefile->strpush;
8817 parsefile->strpush = sp;
8818 } else
8819 sp = parsefile->strpush = &(parsefile->basestrpush);
8820 sp->prevstring = parsenextc;
8821 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008822#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008823 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008824 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008825 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008826 sp->string = s;
8827 }
Eric Andersen2870d962001-07-02 17:27:21 +00008828#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008829 parsenextc = s;
8830 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008831 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008832}
8833
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008834/*
8835 * To handle the "." command, a stack of input files is used. Pushfile
8836 * adds a new entry to the stack and popfile restores the previous level.
8837 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008838static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008839pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008840{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008841 struct parsefile *pf;
8842
8843 parsefile->nleft = parsenleft;
8844 parsefile->lleft = parselleft;
8845 parsefile->nextc = parsenextc;
8846 parsefile->linno = plinno;
8847 pf = ckmalloc(sizeof(*pf));
8848 pf->prev = parsefile;
8849 pf->fd = -1;
8850 pf->strpush = NULL;
8851 pf->basestrpush.prev = NULL;
8852 parsefile = pf;
8853}
8854
8855static void
8856popfile(void)
8857{
8858 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008859
Denis Vlasenkob012b102007-02-19 22:43:01 +00008860 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008861 if (pf->fd >= 0)
8862 close(pf->fd);
8863 if (pf->buf)
8864 free(pf->buf);
8865 while (pf->strpush)
8866 popstring();
8867 parsefile = pf->prev;
8868 free(pf);
8869 parsenleft = parsefile->nleft;
8870 parselleft = parsefile->lleft;
8871 parsenextc = parsefile->nextc;
8872 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008873 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008874}
8875
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008876/*
8877 * Return to top level.
8878 */
8879static void
8880popallfiles(void)
8881{
8882 while (parsefile != &basepf)
8883 popfile();
8884}
8885
8886/*
8887 * Close the file(s) that the shell is reading commands from. Called
8888 * after a fork is done.
8889 */
8890static void
8891closescript(void)
8892{
8893 popallfiles();
8894 if (parsefile->fd > 0) {
8895 close(parsefile->fd);
8896 parsefile->fd = 0;
8897 }
8898}
8899
8900/*
8901 * Like setinputfile, but takes an open file descriptor. Call this with
8902 * interrupts off.
8903 */
8904static void
8905setinputfd(int fd, int push)
8906{
8907 fcntl(fd, F_SETFD, FD_CLOEXEC);
8908 if (push) {
8909 pushfile();
8910 parsefile->buf = 0;
8911 }
8912 parsefile->fd = fd;
8913 if (parsefile->buf == NULL)
8914 parsefile->buf = ckmalloc(IBUFSIZ);
8915 parselleft = parsenleft = 0;
8916 plinno = 1;
8917}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008918
Eric Andersenc470f442003-07-28 09:56:35 +00008919/*
8920 * Set the input to take input from a file. If push is set, push the
8921 * old input onto the stack first.
8922 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008923static int
8924setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008925{
8926 int fd;
8927 int fd2;
8928
Denis Vlasenkob012b102007-02-19 22:43:01 +00008929 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008930 fd = open(fname, O_RDONLY);
8931 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008932 if (flags & INPUT_NOFILE_OK)
8933 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008934 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008935 }
Eric Andersenc470f442003-07-28 09:56:35 +00008936 if (fd < 10) {
8937 fd2 = copyfd(fd, 10);
8938 close(fd);
8939 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008940 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008941 fd = fd2;
8942 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008943 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008944 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008945 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008946 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008947}
8948
Eric Andersencb57d552001-06-28 07:25:16 +00008949/*
8950 * Like setinputfile, but takes input from a string.
8951 */
Eric Andersenc470f442003-07-28 09:56:35 +00008952static void
8953setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008954{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008955 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008956 pushfile();
8957 parsenextc = string;
8958 parsenleft = strlen(string);
8959 parsefile->buf = NULL;
8960 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008961 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008962}
8963
8964
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008965/* ============ mail.c
8966 *
8967 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008968 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008969
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008970#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008971
Eric Andersencb57d552001-06-28 07:25:16 +00008972#define MAXMBOXES 10
8973
Eric Andersenc470f442003-07-28 09:56:35 +00008974/* times of mailboxes */
8975static time_t mailtime[MAXMBOXES];
8976/* Set if MAIL or MAILPATH is changed. */
8977static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008978
Eric Andersencb57d552001-06-28 07:25:16 +00008979/*
Eric Andersenc470f442003-07-28 09:56:35 +00008980 * Print appropriate message(s) if mail has arrived.
8981 * If mail_var_path_changed is set,
8982 * then the value of MAIL has mail_var_path_changed,
8983 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008984 */
Eric Andersenc470f442003-07-28 09:56:35 +00008985static void
8986chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008987{
Eric Andersencb57d552001-06-28 07:25:16 +00008988 const char *mpath;
8989 char *p;
8990 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008991 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008992 struct stackmark smark;
8993 struct stat statb;
8994
Eric Andersencb57d552001-06-28 07:25:16 +00008995 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008996 mpath = mpathset() ? mpathval() : mailval();
8997 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008998 p = padvance(&mpath, nullstr);
8999 if (p == NULL)
9000 break;
9001 if (*p == '\0')
9002 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009003 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009004#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009005 if (q[-1] != '/')
9006 abort();
9007#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009008 q[-1] = '\0'; /* delete trailing '/' */
9009 if (stat(p, &statb) < 0) {
9010 *mtp = 0;
9011 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009012 }
Eric Andersenc470f442003-07-28 09:56:35 +00009013 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9014 fprintf(
9015 stderr, snlfmt,
9016 pathopt ? pathopt : "you have mail"
9017 );
9018 }
9019 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009020 }
Eric Andersenc470f442003-07-28 09:56:35 +00009021 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009022 popstackmark(&smark);
9023}
Eric Andersencb57d552001-06-28 07:25:16 +00009024
Eric Andersenc470f442003-07-28 09:56:35 +00009025static void
9026changemail(const char *val)
9027{
9028 mail_var_path_changed++;
9029}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009030
Denis Vlasenko131ae172007-02-18 13:00:19 +00009031#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009032
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009033
9034/* ============ ??? */
9035
Eric Andersencb57d552001-06-28 07:25:16 +00009036/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009037 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009038 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009039static void
9040setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009041{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009042 char **newparam;
9043 char **ap;
9044 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009045
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009046 for (nparam = 0; argv[nparam]; nparam++);
9047 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9048 while (*argv) {
9049 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009050 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009051 *ap = NULL;
9052 freeparam(&shellparam);
9053 shellparam.malloc = 1;
9054 shellparam.nparam = nparam;
9055 shellparam.p = newparam;
9056#if ENABLE_ASH_GETOPTS
9057 shellparam.optind = 1;
9058 shellparam.optoff = -1;
9059#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009060}
9061
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009062/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009063 * Process shell options. The global variable argptr contains a pointer
9064 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009065 */
9066static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009067minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009068{
9069 int i;
9070
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009071 if (name) {
9072 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009073 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009074 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009075 return;
9076 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009077 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009078 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009079 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009080 out1str("Current option settings\n");
9081 for (i = 0; i < NOPTS; i++)
9082 out1fmt("%-16s%s\n", optnames(i),
9083 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009084}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009085static void
9086setoption(int flag, int val)
9087{
9088 int i;
9089
9090 for (i = 0; i < NOPTS; i++) {
9091 if (optletters(i) == flag) {
9092 optlist[i] = val;
9093 return;
9094 }
9095 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009096 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009097 /* NOTREACHED */
9098}
Eric Andersenc470f442003-07-28 09:56:35 +00009099static void
9100options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009101{
9102 char *p;
9103 int val;
9104 int c;
9105
9106 if (cmdline)
9107 minusc = NULL;
9108 while ((p = *argptr) != NULL) {
9109 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009110 c = *p++;
9111 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009112 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009113 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009114 if (!cmdline) {
9115 /* "-" means turn off -x and -v */
9116 if (p[0] == '\0')
9117 xflag = vflag = 0;
9118 /* "--" means reset params */
9119 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009120 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009121 }
Eric Andersenc470f442003-07-28 09:56:35 +00009122 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009123 }
9124 } else if (c == '+') {
9125 val = 0;
9126 } else {
9127 argptr--;
9128 break;
9129 }
9130 while ((c = *p++) != '\0') {
9131 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00009132 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00009133 } else if (c == 'o') {
9134 minus_o(*argptr, val);
9135 if (*argptr)
9136 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00009137 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009138 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009139 isloginsh = 1;
9140 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009141 } else {
9142 setoption(c, val);
9143 }
9144 }
9145 }
9146}
9147
Eric Andersencb57d552001-06-28 07:25:16 +00009148/*
Eric Andersencb57d552001-06-28 07:25:16 +00009149 * The shift builtin command.
9150 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009151static int
Eric Andersenc470f442003-07-28 09:56:35 +00009152shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009153{
9154 int n;
9155 char **ap1, **ap2;
9156
9157 n = 1;
9158 if (argc > 1)
9159 n = number(argv[1]);
9160 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009161 ash_msg_and_raise_error("can't shift that many");
9162 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009163 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009164 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009165 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009166 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009167 }
9168 ap2 = shellparam.p;
9169 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009170#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009171 shellparam.optind = 1;
9172 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009173#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009174 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009175 return 0;
9176}
9177
Eric Andersencb57d552001-06-28 07:25:16 +00009178/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009179 * POSIX requires that 'set' (but not export or readonly) output the
9180 * variables in lexicographic order - by the locale's collating order (sigh).
9181 * Maybe we could keep them in an ordered balanced binary tree
9182 * instead of hashed lists.
9183 * For now just roll 'em through qsort for printing...
9184 */
9185static int
9186showvars(const char *sep_prefix, int on, int off)
9187{
9188 const char *sep;
9189 char **ep, **epend;
9190
9191 ep = listvars(on, off, &epend);
9192 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9193
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009194 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009195
9196 for (; ep < epend; ep++) {
9197 const char *p;
9198 const char *q;
9199
9200 p = strchrnul(*ep, '=');
9201 q = nullstr;
9202 if (*p)
9203 q = single_quote(++p);
9204 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9205 }
9206 return 0;
9207}
9208
9209/*
Eric Andersencb57d552001-06-28 07:25:16 +00009210 * The set command builtin.
9211 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009212static int
Eric Andersenc470f442003-07-28 09:56:35 +00009213setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009214{
9215 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009216 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009217 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009218 options(0);
9219 optschanged();
9220 if (*argptr != NULL) {
9221 setparam(argptr);
9222 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009223 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009224 return 0;
9225}
9226
Denis Vlasenko131ae172007-02-18 13:00:19 +00009227#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009228/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009229static void
9230change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009231{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009232 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009233 /* "get", generate */
9234 char buf[16];
9235
9236 rseed = rseed * 1103515245 + 12345;
9237 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9238 /* set without recursion */
9239 setvar(vrandom.text, buf, VNOFUNC);
9240 vrandom.flags &= ~VNOFUNC;
9241 } else {
9242 /* set/reset */
9243 rseed = strtoul(value, (char **)NULL, 10);
9244 }
Eric Andersenef02f822004-03-11 13:34:24 +00009245}
Eric Andersen16767e22004-03-16 05:14:10 +00009246#endif
9247
Denis Vlasenko131ae172007-02-18 13:00:19 +00009248#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009249static int
Eric Andersenc470f442003-07-28 09:56:35 +00009250getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009251{
9252 char *p, *q;
9253 char c = '?';
9254 int done = 0;
9255 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009256 char s[12];
9257 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009258
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009259 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009260 return 1;
9261 optnext = optfirst + *param_optind - 1;
9262
9263 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009264 p = NULL;
9265 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009266 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009267 if (p == NULL || *p == '\0') {
9268 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009269 p = *optnext;
9270 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009271 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009272 p = NULL;
9273 done = 1;
9274 goto out;
9275 }
9276 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009277 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009278 goto atend;
9279 }
9280
9281 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009282 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009283 if (*q == '\0') {
9284 if (optstr[0] == ':') {
9285 s[0] = c;
9286 s[1] = '\0';
9287 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009288 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009289 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009290 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009291 }
9292 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009293 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009294 }
9295 if (*++q == ':')
9296 q++;
9297 }
9298
9299 if (*++q == ':') {
9300 if (*p == '\0' && (p = *optnext) == NULL) {
9301 if (optstr[0] == ':') {
9302 s[0] = c;
9303 s[1] = '\0';
9304 err |= setvarsafe("OPTARG", s, 0);
9305 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009306 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009307 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009308 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009309 c = '?';
9310 }
Eric Andersenc470f442003-07-28 09:56:35 +00009311 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009312 }
9313
9314 if (p == *optnext)
9315 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009316 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009317 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009318 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009319 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009320 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009321 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009322 *param_optind = optnext - optfirst + 1;
9323 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009324 err |= setvarsafe("OPTIND", s, VNOFUNC);
9325 s[0] = c;
9326 s[1] = '\0';
9327 err |= setvarsafe(optvar, s, 0);
9328 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009329 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009330 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009331 flush_stdout_stderr();
9332 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009333 }
9334 return done;
9335}
Eric Andersenc470f442003-07-28 09:56:35 +00009336
9337/*
9338 * The getopts builtin. Shellparam.optnext points to the next argument
9339 * to be processed. Shellparam.optptr points to the next character to
9340 * be processed in the current argument. If shellparam.optnext is NULL,
9341 * then it's the first time getopts has been called.
9342 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009343static int
Eric Andersenc470f442003-07-28 09:56:35 +00009344getoptscmd(int argc, char **argv)
9345{
9346 char **optbase;
9347
9348 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009349 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009350 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009351 optbase = shellparam.p;
9352 if (shellparam.optind > shellparam.nparam + 1) {
9353 shellparam.optind = 1;
9354 shellparam.optoff = -1;
9355 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009356 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009357 optbase = &argv[3];
9358 if (shellparam.optind > argc - 2) {
9359 shellparam.optind = 1;
9360 shellparam.optoff = -1;
9361 }
9362 }
9363
9364 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009365 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009366}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009367#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009368
Eric Andersencb57d552001-06-28 07:25:16 +00009369
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009370/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009371
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009372static int tokpushback; /* last token pushed back */
9373#define NEOF ((union node *)&tokpushback)
9374static int parsebackquote; /* nonzero if we are inside backquotes */
9375static int lasttoken; /* last token read */
9376static char *wordtext; /* text of last word returned by readtoken */
9377static struct nodelist *backquotelist;
9378static union node *redirnode;
9379static struct heredoc *heredoc;
9380static int quoteflag; /* set if (part of) last token was quoted */
9381
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009382static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9383static void
9384raise_error_syntax(const char *msg)
9385{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009386 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009387 /* NOTREACHED */
9388}
9389
9390/*
9391 * Called when an unexpected token is read during the parse. The argument
9392 * is the token that is expected, or -1 if more than one type of token can
9393 * occur at this point.
9394 */
9395static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9396static void
9397raise_error_unexpected_syntax(int token)
9398{
9399 char msg[64];
9400 int l;
9401
9402 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9403 if (token >= 0)
9404 sprintf(msg + l, " (expecting %s)", tokname(token));
9405 raise_error_syntax(msg);
9406 /* NOTREACHED */
9407}
Eric Andersencb57d552001-06-28 07:25:16 +00009408
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009409#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009410
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009411struct heredoc {
9412 struct heredoc *next; /* next here document in list */
9413 union node *here; /* redirection node */
9414 char *eofmark; /* string indicating end of input */
9415 int striptabs; /* if set, strip leading tabs */
9416};
Eric Andersencb57d552001-06-28 07:25:16 +00009417
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009418static struct heredoc *heredoclist; /* list of here documents to read */
9419
9420/* parsing is heavily cross-recursive, need these forward decls */
9421static union node *andor(void);
9422static union node *pipeline(void);
9423static union node *parse_command(void);
9424static void parseheredoc(void);
9425static char peektoken(void);
9426static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009427
Eric Andersenc470f442003-07-28 09:56:35 +00009428static union node *
9429list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009430{
9431 union node *n1, *n2, *n3;
9432 int tok;
9433
Eric Andersenc470f442003-07-28 09:56:35 +00009434 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9435 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009436 return NULL;
9437 n1 = NULL;
9438 for (;;) {
9439 n2 = andor();
9440 tok = readtoken();
9441 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009442 if (n2->type == NPIPE) {
9443 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009444 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009445 if (n2->type != NREDIR) {
9446 n3 = stalloc(sizeof(struct nredir));
9447 n3->nredir.n = n2;
9448 n3->nredir.redirect = NULL;
9449 n2 = n3;
9450 }
9451 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009452 }
9453 }
9454 if (n1 == NULL) {
9455 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009456 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009457 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009458 n3->type = NSEMI;
9459 n3->nbinary.ch1 = n1;
9460 n3->nbinary.ch2 = n2;
9461 n1 = n3;
9462 }
9463 switch (tok) {
9464 case TBACKGND:
9465 case TSEMI:
9466 tok = readtoken();
9467 /* fall through */
9468 case TNL:
9469 if (tok == TNL) {
9470 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009471 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009472 return n1;
9473 } else {
9474 tokpushback++;
9475 }
Eric Andersenc470f442003-07-28 09:56:35 +00009476 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009477 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009478 return n1;
9479 break;
9480 case TEOF:
9481 if (heredoclist)
9482 parseheredoc();
9483 else
Eric Andersenc470f442003-07-28 09:56:35 +00009484 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009485 return n1;
9486 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009487 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009488 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009489 tokpushback++;
9490 return n1;
9491 }
9492 }
9493}
9494
Eric Andersenc470f442003-07-28 09:56:35 +00009495static union node *
9496andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009497{
Eric Andersencb57d552001-06-28 07:25:16 +00009498 union node *n1, *n2, *n3;
9499 int t;
9500
Eric Andersencb57d552001-06-28 07:25:16 +00009501 n1 = pipeline();
9502 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009503 t = readtoken();
9504 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009505 t = NAND;
9506 } else if (t == TOR) {
9507 t = NOR;
9508 } else {
9509 tokpushback++;
9510 return n1;
9511 }
Eric Andersenc470f442003-07-28 09:56:35 +00009512 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009513 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009514 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009515 n3->type = t;
9516 n3->nbinary.ch1 = n1;
9517 n3->nbinary.ch2 = n2;
9518 n1 = n3;
9519 }
9520}
9521
Eric Andersenc470f442003-07-28 09:56:35 +00009522static union node *
9523pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009524{
Eric Andersencb57d552001-06-28 07:25:16 +00009525 union node *n1, *n2, *pipenode;
9526 struct nodelist *lp, *prev;
9527 int negate;
9528
9529 negate = 0;
9530 TRACE(("pipeline: entered\n"));
9531 if (readtoken() == TNOT) {
9532 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009533 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009534 } else
9535 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009536 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009537 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009538 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009539 pipenode->type = NPIPE;
9540 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009541 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009542 pipenode->npipe.cmdlist = lp;
9543 lp->n = n1;
9544 do {
9545 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009546 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009547 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009548 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009549 prev->next = lp;
9550 } while (readtoken() == TPIPE);
9551 lp->next = NULL;
9552 n1 = pipenode;
9553 }
9554 tokpushback++;
9555 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009556 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009557 n2->type = NNOT;
9558 n2->nnot.com = n1;
9559 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009560 }
9561 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009562}
9563
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009564static union node *
9565makename(void)
9566{
9567 union node *n;
9568
9569 n = stalloc(sizeof(struct narg));
9570 n->type = NARG;
9571 n->narg.next = NULL;
9572 n->narg.text = wordtext;
9573 n->narg.backquote = backquotelist;
9574 return n;
9575}
9576
9577static void
9578fixredir(union node *n, const char *text, int err)
9579{
9580 TRACE(("Fix redir %s %d\n", text, err));
9581 if (!err)
9582 n->ndup.vname = NULL;
9583
9584 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009585 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009586 else if (LONE_DASH(text))
9587 n->ndup.dupfd = -1;
9588 else {
9589 if (err)
9590 raise_error_syntax("Bad fd number");
9591 n->ndup.vname = makename();
9592 }
9593}
9594
9595/*
9596 * Returns true if the text contains nothing to expand (no dollar signs
9597 * or backquotes).
9598 */
9599static int
9600noexpand(char *text)
9601{
9602 char *p;
9603 char c;
9604
9605 p = text;
9606 while ((c = *p++) != '\0') {
9607 if (c == CTLQUOTEMARK)
9608 continue;
9609 if (c == CTLESC)
9610 p++;
9611 else if (SIT(c, BASESYNTAX) == CCTL)
9612 return 0;
9613 }
9614 return 1;
9615}
9616
9617static void
9618parsefname(void)
9619{
9620 union node *n = redirnode;
9621
9622 if (readtoken() != TWORD)
9623 raise_error_unexpected_syntax(-1);
9624 if (n->type == NHERE) {
9625 struct heredoc *here = heredoc;
9626 struct heredoc *p;
9627 int i;
9628
9629 if (quoteflag == 0)
9630 n->type = NXHERE;
9631 TRACE(("Here document %d\n", n->type));
9632 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9633 raise_error_syntax("Illegal eof marker for << redirection");
9634 rmescapes(wordtext);
9635 here->eofmark = wordtext;
9636 here->next = NULL;
9637 if (heredoclist == NULL)
9638 heredoclist = here;
9639 else {
9640 for (p = heredoclist; p->next; p = p->next);
9641 p->next = here;
9642 }
9643 } else if (n->type == NTOFD || n->type == NFROMFD) {
9644 fixredir(n, wordtext, 0);
9645 } else {
9646 n->nfile.fname = makename();
9647 }
9648}
Eric Andersencb57d552001-06-28 07:25:16 +00009649
Eric Andersenc470f442003-07-28 09:56:35 +00009650static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009651simplecmd(void)
9652{
9653 union node *args, **app;
9654 union node *n = NULL;
9655 union node *vars, **vpp;
9656 union node **rpp, *redir;
9657 int savecheckkwd;
9658
9659 args = NULL;
9660 app = &args;
9661 vars = NULL;
9662 vpp = &vars;
9663 redir = NULL;
9664 rpp = &redir;
9665
9666 savecheckkwd = CHKALIAS;
9667 for (;;) {
9668 checkkwd = savecheckkwd;
9669 switch (readtoken()) {
9670 case TWORD:
9671 n = stalloc(sizeof(struct narg));
9672 n->type = NARG;
9673 n->narg.text = wordtext;
9674 n->narg.backquote = backquotelist;
9675 if (savecheckkwd && isassignment(wordtext)) {
9676 *vpp = n;
9677 vpp = &n->narg.next;
9678 } else {
9679 *app = n;
9680 app = &n->narg.next;
9681 savecheckkwd = 0;
9682 }
9683 break;
9684 case TREDIR:
9685 *rpp = n = redirnode;
9686 rpp = &n->nfile.next;
9687 parsefname(); /* read name of redirection file */
9688 break;
9689 case TLP:
9690 if (args && app == &args->narg.next
9691 && !vars && !redir
9692 ) {
9693 struct builtincmd *bcmd;
9694 const char *name;
9695
9696 /* We have a function */
9697 if (readtoken() != TRP)
9698 raise_error_unexpected_syntax(TRP);
9699 name = n->narg.text;
9700 if (!goodname(name)
9701 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9702 ) {
9703 raise_error_syntax("Bad function name");
9704 }
9705 n->type = NDEFUN;
9706 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9707 n->narg.next = parse_command();
9708 return n;
9709 }
9710 /* fall through */
9711 default:
9712 tokpushback++;
9713 goto out;
9714 }
9715 }
9716 out:
9717 *app = NULL;
9718 *vpp = NULL;
9719 *rpp = NULL;
9720 n = stalloc(sizeof(struct ncmd));
9721 n->type = NCMD;
9722 n->ncmd.args = args;
9723 n->ncmd.assign = vars;
9724 n->ncmd.redirect = redir;
9725 return n;
9726}
9727
9728static union node *
9729parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009730{
Eric Andersencb57d552001-06-28 07:25:16 +00009731 union node *n1, *n2;
9732 union node *ap, **app;
9733 union node *cp, **cpp;
9734 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009735 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009736 int t;
9737
9738 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009739 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009740
Eric Andersencb57d552001-06-28 07:25:16 +00009741 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009742 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009743 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009744 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009745 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009746 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009747 n1->type = NIF;
9748 n1->nif.test = list(0);
9749 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009750 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009751 n1->nif.ifpart = list(0);
9752 n2 = n1;
9753 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009754 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009755 n2 = n2->nif.elsepart;
9756 n2->type = NIF;
9757 n2->nif.test = list(0);
9758 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009759 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009760 n2->nif.ifpart = list(0);
9761 }
9762 if (lasttoken == TELSE)
9763 n2->nif.elsepart = list(0);
9764 else {
9765 n2->nif.elsepart = NULL;
9766 tokpushback++;
9767 }
Eric Andersenc470f442003-07-28 09:56:35 +00009768 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009769 break;
9770 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009771 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009772 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009773 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009774 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009775 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009776 got = readtoken();
9777 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009778 TRACE(("expecting DO got %s %s\n", tokname(got),
9779 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009780 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009781 }
9782 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009783 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009784 break;
9785 }
9786 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009787 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009788 raise_error_syntax("Bad for loop variable");
9789 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009790 n1->type = NFOR;
9791 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009792 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009793 if (readtoken() == TIN) {
9794 app = &ap;
9795 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009796 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009797 n2->type = NARG;
9798 n2->narg.text = wordtext;
9799 n2->narg.backquote = backquotelist;
9800 *app = n2;
9801 app = &n2->narg.next;
9802 }
9803 *app = NULL;
9804 n1->nfor.args = ap;
9805 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009806 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009807 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009808 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009809 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009810 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009811 n2->narg.backquote = NULL;
9812 n2->narg.next = NULL;
9813 n1->nfor.args = n2;
9814 /*
9815 * Newline or semicolon here is optional (but note
9816 * that the original Bourne shell only allowed NL).
9817 */
9818 if (lasttoken != TNL && lasttoken != TSEMI)
9819 tokpushback++;
9820 }
Eric Andersenc470f442003-07-28 09:56:35 +00009821 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009822 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009823 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009824 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009825 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009826 break;
9827 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009828 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009829 n1->type = NCASE;
9830 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009831 raise_error_unexpected_syntax(TWORD);
9832 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009833 n2->type = NARG;
9834 n2->narg.text = wordtext;
9835 n2->narg.backquote = backquotelist;
9836 n2->narg.next = NULL;
9837 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009838 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009839 } while (readtoken() == TNL);
9840 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009841 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009842 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009843 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009844 checkkwd = CHKNL | CHKKWD;
9845 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009846 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009847 if (lasttoken == TLP)
9848 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009849 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009850 cp->type = NCLIST;
9851 app = &cp->nclist.pattern;
9852 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009853 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009854 ap->type = NARG;
9855 ap->narg.text = wordtext;
9856 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009857 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009858 break;
9859 app = &ap->narg.next;
9860 readtoken();
9861 }
9862 ap->narg.next = NULL;
9863 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009864 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009865 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009866
Eric Andersenc470f442003-07-28 09:56:35 +00009867 cpp = &cp->nclist.next;
9868
9869 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009870 t = readtoken();
9871 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009872 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009873 raise_error_unexpected_syntax(TENDCASE);
9874 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009875 }
Eric Andersenc470f442003-07-28 09:56:35 +00009876 }
Eric Andersencb57d552001-06-28 07:25:16 +00009877 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009878 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009879 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009880 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009881 n1->type = NSUBSHELL;
9882 n1->nredir.n = list(0);
9883 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009884 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009885 break;
9886 case TBEGIN:
9887 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009888 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009889 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009890 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009891 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009892 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009893 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009894 }
9895
Eric Andersenc470f442003-07-28 09:56:35 +00009896 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009897 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009898
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009899 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009900 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009901 checkkwd = CHKKWD | CHKALIAS;
9902 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009903 while (readtoken() == TREDIR) {
9904 *rpp = n2 = redirnode;
9905 rpp = &n2->nfile.next;
9906 parsefname();
9907 }
9908 tokpushback++;
9909 *rpp = NULL;
9910 if (redir) {
9911 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009912 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009913 n2->type = NREDIR;
9914 n2->nredir.n = n1;
9915 n1 = n2;
9916 }
9917 n1->nredir.redirect = redir;
9918 }
Eric Andersencb57d552001-06-28 07:25:16 +00009919 return n1;
9920}
9921
Eric Andersencb57d552001-06-28 07:25:16 +00009922/*
9923 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9924 * is not NULL, read a here document. In the latter case, eofmark is the
9925 * word which marks the end of the document and striptabs is true if
9926 * leading tabs should be stripped from the document. The argument firstc
9927 * is the first character of the input token or document.
9928 *
9929 * Because C does not have internal subroutines, I have simulated them
9930 * using goto's to implement the subroutine linkage. The following macros
9931 * will run code that appears at the end of readtoken1.
9932 */
9933
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009934static int parsebackquote; /* nonzero if we are inside backquotes */
9935
Eric Andersen2870d962001-07-02 17:27:21 +00009936#define CHECKEND() {goto checkend; checkend_return:;}
9937#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9938#define PARSESUB() {goto parsesub; parsesub_return:;}
9939#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9940#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9941#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009942
9943static int
Eric Andersenc470f442003-07-28 09:56:35 +00009944readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009945{
Eric Andersencb57d552001-06-28 07:25:16 +00009946 int c = firstc;
9947 char *out;
9948 int len;
9949 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009950 struct nodelist *bqlist = 0;
9951 int quotef = 0;
9952 int dblquote = 0;
9953 int varnest = 0; /* levels of variables expansion */
9954 int arinest = 0; /* levels of arithmetic expansion */
9955 int parenlevel = 0; /* levels of parens in arithmetic */
9956 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9957 int oldstyle = 0;
9958 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009959#if __GNUC__
9960 /* Avoid longjmp clobbering */
9961 (void) &out;
9962 (void) &quotef;
9963 (void) &dblquote;
9964 (void) &varnest;
9965 (void) &arinest;
9966 (void) &parenlevel;
9967 (void) &dqvarnest;
9968 (void) &oldstyle;
9969 (void) &prevsyntax;
9970 (void) &syntax;
9971#endif
9972
9973 startlinno = plinno;
9974 dblquote = 0;
9975 if (syntax == DQSYNTAX)
9976 dblquote = 1;
9977 quotef = 0;
9978 bqlist = NULL;
9979 varnest = 0;
9980 arinest = 0;
9981 parenlevel = 0;
9982 dqvarnest = 0;
9983
9984 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009985 loop: { /* for each line, until end of word */
9986 CHECKEND(); /* set c to PEOF if at end of here document */
9987 for (;;) { /* until end of line or end of word */
9988 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009989 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009990 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009991 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009992 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009993 USTPUTC(c, out);
9994 plinno++;
9995 if (doprompt)
9996 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009997 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009998 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009999 case CWORD:
10000 USTPUTC(c, out);
10001 break;
10002 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010003 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010004 USTPUTC(CTLESC, out);
10005 USTPUTC(c, out);
10006 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010007 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010008 c = pgetc2();
10009 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010010 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010011 USTPUTC('\\', out);
10012 pungetc();
10013 } else if (c == '\n') {
10014 if (doprompt)
10015 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010016 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010017 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010018 c != '\\' && c != '`' &&
10019 c != '$' && (
10020 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010021 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010022 ) {
10023 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010024 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010025 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010026 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010027 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010028 USTPUTC(c, out);
10029 quotef++;
10030 }
10031 break;
10032 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010033 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010034 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010035 if (eofmark == NULL) {
10036 USTPUTC(CTLQUOTEMARK, out);
10037 }
Eric Andersencb57d552001-06-28 07:25:16 +000010038 break;
10039 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010040 syntax = DQSYNTAX;
10041 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010042 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010043 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010044 if (eofmark != NULL && arinest == 0
10045 && varnest == 0
10046 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010047 USTPUTC(c, out);
10048 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010049 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010050 syntax = BASESYNTAX;
10051 dblquote = 0;
10052 }
10053 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +000010054 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010055 }
10056 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010057 case CVAR: /* '$' */
10058 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010059 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010060 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010061 if (varnest > 0) {
10062 varnest--;
10063 if (dqvarnest > 0) {
10064 dqvarnest--;
10065 }
10066 USTPUTC(CTLENDVAR, out);
10067 } else {
10068 USTPUTC(c, out);
10069 }
10070 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010071#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010072 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010073 parenlevel++;
10074 USTPUTC(c, out);
10075 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010076 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010077 if (parenlevel > 0) {
10078 USTPUTC(c, out);
10079 --parenlevel;
10080 } else {
10081 if (pgetc() == ')') {
10082 if (--arinest == 0) {
10083 USTPUTC(CTLENDARI, out);
10084 syntax = prevsyntax;
10085 if (syntax == DQSYNTAX)
10086 dblquote = 1;
10087 else
10088 dblquote = 0;
10089 } else
10090 USTPUTC(')', out);
10091 } else {
10092 /*
10093 * unbalanced parens
10094 * (don't 2nd guess - no error)
10095 */
10096 pungetc();
10097 USTPUTC(')', out);
10098 }
10099 }
10100 break;
10101#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010102 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010103 PARSEBACKQOLD();
10104 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010105 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010106 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010107 case CIGN:
10108 break;
10109 default:
10110 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010111 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010112#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010113 if (c != PEOA)
10114#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010115 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010116
Eric Andersencb57d552001-06-28 07:25:16 +000010117 }
10118 c = pgetc_macro();
10119 }
10120 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010121 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010122#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010123 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010124 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010125#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010126 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010127 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010128 if (varnest != 0) {
10129 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010130 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010131 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010132 }
10133 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010134 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010135 out = stackblock();
10136 if (eofmark == NULL) {
10137 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010138 && quotef == 0
10139 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010140 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010141 PARSEREDIR();
10142 return lasttoken = TREDIR;
10143 } else {
10144 pungetc();
10145 }
10146 }
10147 quoteflag = quotef;
10148 backquotelist = bqlist;
10149 grabstackblock(len);
10150 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010151 lasttoken = TWORD;
10152 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010153/* end of readtoken routine */
10154
Eric Andersencb57d552001-06-28 07:25:16 +000010155/*
10156 * Check to see whether we are at the end of the here document. When this
10157 * is called, c is set to the first character of the next input line. If
10158 * we are at the end of the here document, this routine sets the c to PEOF.
10159 */
Eric Andersenc470f442003-07-28 09:56:35 +000010160checkend: {
10161 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010162#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010163 if (c == PEOA) {
10164 c = pgetc2();
10165 }
10166#endif
10167 if (striptabs) {
10168 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010169 c = pgetc2();
10170 }
Eric Andersenc470f442003-07-28 09:56:35 +000010171 }
10172 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010173 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010174 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010175
Eric Andersenc470f442003-07-28 09:56:35 +000010176 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010177 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010178 if (*p == '\n' && *q == '\0') {
10179 c = PEOF;
10180 plinno++;
10181 needprompt = doprompt;
10182 } else {
10183 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010184 }
10185 }
10186 }
10187 }
Eric Andersenc470f442003-07-28 09:56:35 +000010188 goto checkend_return;
10189}
Eric Andersencb57d552001-06-28 07:25:16 +000010190
Eric Andersencb57d552001-06-28 07:25:16 +000010191/*
10192 * Parse a redirection operator. The variable "out" points to a string
10193 * specifying the fd to be redirected. The variable "c" contains the
10194 * first character of the redirection operator.
10195 */
Eric Andersenc470f442003-07-28 09:56:35 +000010196parseredir: {
10197 char fd = *out;
10198 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010199
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010200 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010201 if (c == '>') {
10202 np->nfile.fd = 1;
10203 c = pgetc();
10204 if (c == '>')
10205 np->type = NAPPEND;
10206 else if (c == '|')
10207 np->type = NCLOBBER;
10208 else if (c == '&')
10209 np->type = NTOFD;
10210 else {
10211 np->type = NTO;
10212 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010213 }
Eric Andersenc470f442003-07-28 09:56:35 +000010214 } else { /* c == '<' */
10215 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010216 c = pgetc();
10217 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010218 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010219 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010220 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010221 np->nfile.fd = 0;
10222 }
10223 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010224 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010225 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010226 c = pgetc();
10227 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010228 heredoc->striptabs = 1;
10229 } else {
10230 heredoc->striptabs = 0;
10231 pungetc();
10232 }
10233 break;
10234
10235 case '&':
10236 np->type = NFROMFD;
10237 break;
10238
10239 case '>':
10240 np->type = NFROMTO;
10241 break;
10242
10243 default:
10244 np->type = NFROM;
10245 pungetc();
10246 break;
10247 }
Eric Andersencb57d552001-06-28 07:25:16 +000010248 }
Eric Andersenc470f442003-07-28 09:56:35 +000010249 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010250 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010251 redirnode = np;
10252 goto parseredir_return;
10253}
Eric Andersencb57d552001-06-28 07:25:16 +000010254
Eric Andersencb57d552001-06-28 07:25:16 +000010255/*
10256 * Parse a substitution. At this point, we have read the dollar sign
10257 * and nothing else.
10258 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010259
10260/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10261 * (assuming ascii char codes, as the original implementation did) */
10262#define is_special(c) \
10263 ((((unsigned int)c) - 33 < 32) \
10264 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010265parsesub: {
10266 int subtype;
10267 int typeloc;
10268 int flags;
10269 char *p;
10270 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010271
Eric Andersenc470f442003-07-28 09:56:35 +000010272 c = pgetc();
10273 if (
10274 c <= PEOA_OR_PEOF ||
10275 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10276 ) {
10277 USTPUTC('$', out);
10278 pungetc();
10279 } else if (c == '(') { /* $(command) or $((arith)) */
10280 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010281#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010282 PARSEARITH();
10283#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010284 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010285#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010286 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010287 pungetc();
10288 PARSEBACKQNEW();
10289 }
10290 } else {
10291 USTPUTC(CTLVAR, out);
10292 typeloc = out - (char *)stackblock();
10293 USTPUTC(VSNORMAL, out);
10294 subtype = VSNORMAL;
10295 if (c == '{') {
10296 c = pgetc();
10297 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010298 c = pgetc();
10299 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010300 c = '#';
10301 else
10302 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010303 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010304 subtype = 0;
10305 }
10306 if (c > PEOA_OR_PEOF && is_name(c)) {
10307 do {
10308 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010309 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010310 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010311 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010312 do {
10313 STPUTC(c, out);
10314 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010315 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010316 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010317 USTPUTC(c, out);
10318 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010319 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010320 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010321
Eric Andersenc470f442003-07-28 09:56:35 +000010322 STPUTC('=', out);
10323 flags = 0;
10324 if (subtype == 0) {
10325 switch (c) {
10326 case ':':
10327 flags = VSNUL;
10328 c = pgetc();
10329 /*FALLTHROUGH*/
10330 default:
10331 p = strchr(types, c);
10332 if (p == NULL)
10333 goto badsub;
10334 subtype = p - types + VSNORMAL;
10335 break;
10336 case '%':
10337 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010338 {
10339 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010340 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010341 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010342 c = pgetc();
10343 if (c == cc)
10344 subtype++;
10345 else
10346 pungetc();
10347 break;
10348 }
10349 }
Eric Andersenc470f442003-07-28 09:56:35 +000010350 } else {
10351 pungetc();
10352 }
10353 if (dblquote || arinest)
10354 flags |= VSQUOTE;
10355 *((char *)stackblock() + typeloc) = subtype | flags;
10356 if (subtype != VSNORMAL) {
10357 varnest++;
10358 if (dblquote || arinest) {
10359 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010360 }
10361 }
10362 }
Eric Andersenc470f442003-07-28 09:56:35 +000010363 goto parsesub_return;
10364}
Eric Andersencb57d552001-06-28 07:25:16 +000010365
Eric Andersencb57d552001-06-28 07:25:16 +000010366/*
10367 * Called to parse command substitutions. Newstyle is set if the command
10368 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10369 * list of commands (passed by reference), and savelen is the number of
10370 * characters on the top of the stack which must be preserved.
10371 */
Eric Andersenc470f442003-07-28 09:56:35 +000010372parsebackq: {
10373 struct nodelist **nlpp;
10374 int savepbq;
10375 union node *n;
10376 char *volatile str;
10377 struct jmploc jmploc;
10378 struct jmploc *volatile savehandler;
10379 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010380 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010381#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010382 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010383#endif
10384
Eric Andersenc470f442003-07-28 09:56:35 +000010385 savepbq = parsebackquote;
10386 if (setjmp(jmploc.loc)) {
10387 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010388 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010389 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010390 exception_handler = savehandler;
10391 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010392 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010393 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010394 str = NULL;
10395 savelen = out - (char *)stackblock();
10396 if (savelen > 0) {
10397 str = ckmalloc(savelen);
10398 memcpy(str, stackblock(), savelen);
10399 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010400 savehandler = exception_handler;
10401 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010402 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010403 if (oldstyle) {
10404 /* We must read until the closing backquote, giving special
10405 treatment to some slashes, and then push the string and
10406 reread it as input, interpreting it normally. */
10407 char *pout;
10408 int pc;
10409 size_t psavelen;
10410 char *pstr;
10411
10412
10413 STARTSTACKSTR(pout);
10414 for (;;) {
10415 if (needprompt) {
10416 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010417 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010418 pc = pgetc();
10419 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010420 case '`':
10421 goto done;
10422
10423 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010424 pc = pgetc();
10425 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010426 plinno++;
10427 if (doprompt)
10428 setprompt(2);
10429 /*
10430 * If eating a newline, avoid putting
10431 * the newline into the new character
10432 * stream (via the STPUTC after the
10433 * switch).
10434 */
10435 continue;
10436 }
10437 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010438 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010439 STPUTC('\\', pout);
10440 if (pc > PEOA_OR_PEOF) {
10441 break;
10442 }
10443 /* fall through */
10444
10445 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010446#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010447 case PEOA:
10448#endif
10449 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010450 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010451
10452 case '\n':
10453 plinno++;
10454 needprompt = doprompt;
10455 break;
10456
10457 default:
10458 break;
10459 }
10460 STPUTC(pc, pout);
10461 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010462 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010463 STPUTC('\0', pout);
10464 psavelen = pout - (char *)stackblock();
10465 if (psavelen > 0) {
10466 pstr = grabstackstr(pout);
10467 setinputstring(pstr);
10468 }
10469 }
10470 nlpp = &bqlist;
10471 while (*nlpp)
10472 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010473 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010474 (*nlpp)->next = NULL;
10475 parsebackquote = oldstyle;
10476
10477 if (oldstyle) {
10478 saveprompt = doprompt;
10479 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010480 }
10481
Eric Andersenc470f442003-07-28 09:56:35 +000010482 n = list(2);
10483
10484 if (oldstyle)
10485 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010486 else if (readtoken() != TRP)
10487 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010488
10489 (*nlpp)->n = n;
10490 if (oldstyle) {
10491 /*
10492 * Start reading from old file again, ignoring any pushed back
10493 * tokens left from the backquote parsing
10494 */
10495 popfile();
10496 tokpushback = 0;
10497 }
10498 while (stackblocksize() <= savelen)
10499 growstackblock();
10500 STARTSTACKSTR(out);
10501 if (str) {
10502 memcpy(out, str, savelen);
10503 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010504 INT_OFF;
10505 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010506 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010507 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010508 }
10509 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010510 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010511 if (arinest || dblquote)
10512 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10513 else
10514 USTPUTC(CTLBACKQ, out);
10515 if (oldstyle)
10516 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010517 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010518}
10519
Denis Vlasenko131ae172007-02-18 13:00:19 +000010520#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010521/*
10522 * Parse an arithmetic expansion (indicate start of one and set state)
10523 */
Eric Andersenc470f442003-07-28 09:56:35 +000010524parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010525 if (++arinest == 1) {
10526 prevsyntax = syntax;
10527 syntax = ARISYNTAX;
10528 USTPUTC(CTLARI, out);
10529 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010530 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010531 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010532 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010533 } else {
10534 /*
10535 * we collapse embedded arithmetic expansion to
10536 * parenthesis, which should be equivalent
10537 */
10538 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010539 }
Eric Andersenc470f442003-07-28 09:56:35 +000010540 goto parsearith_return;
10541}
10542#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010543
Eric Andersenc470f442003-07-28 09:56:35 +000010544} /* end of readtoken */
10545
Eric Andersencb57d552001-06-28 07:25:16 +000010546/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010547 * Read the next input token.
10548 * If the token is a word, we set backquotelist to the list of cmds in
10549 * backquotes. We set quoteflag to true if any part of the word was
10550 * quoted.
10551 * If the token is TREDIR, then we set redirnode to a structure containing
10552 * the redirection.
10553 * In all cases, the variable startlinno is set to the number of the line
10554 * on which the token starts.
10555 *
10556 * [Change comment: here documents and internal procedures]
10557 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10558 * word parsing code into a separate routine. In this case, readtoken
10559 * doesn't need to have any internal procedures, but parseword does.
10560 * We could also make parseoperator in essence the main routine, and
10561 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010562 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010563#define NEW_xxreadtoken
10564#ifdef NEW_xxreadtoken
10565/* singles must be first! */
10566static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010567
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010568static const char xxreadtoken_tokens[] = {
10569 TNL, TLP, TRP, /* only single occurrence allowed */
10570 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10571 TEOF, /* corresponds to trailing nul */
10572 TAND, TOR, TENDCASE, /* if double occurrence */
10573};
10574
10575#define xxreadtoken_doubles \
10576 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10577#define xxreadtoken_singles \
10578 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10579
10580static int
10581xxreadtoken(void)
10582{
10583 int c;
10584
10585 if (tokpushback) {
10586 tokpushback = 0;
10587 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010588 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010589 if (needprompt) {
10590 setprompt(2);
10591 }
10592 startlinno = plinno;
10593 for (;;) { /* until token or start of word found */
10594 c = pgetc_macro();
10595
10596 if ((c != ' ') && (c != '\t')
10597#if ENABLE_ASH_ALIAS
10598 && (c != PEOA)
10599#endif
10600 ) {
10601 if (c == '#') {
10602 while ((c = pgetc()) != '\n' && c != PEOF);
10603 pungetc();
10604 } else if (c == '\\') {
10605 if (pgetc() != '\n') {
10606 pungetc();
10607 goto READTOKEN1;
10608 }
10609 startlinno = ++plinno;
10610 if (doprompt)
10611 setprompt(2);
10612 } else {
10613 const char *p
10614 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10615
10616 if (c != PEOF) {
10617 if (c == '\n') {
10618 plinno++;
10619 needprompt = doprompt;
10620 }
10621
10622 p = strchr(xxreadtoken_chars, c);
10623 if (p == NULL) {
10624 READTOKEN1:
10625 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10626 }
10627
10628 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10629 if (pgetc() == *p) { /* double occurrence? */
10630 p += xxreadtoken_doubles + 1;
10631 } else {
10632 pungetc();
10633 }
10634 }
10635 }
10636 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10637 }
10638 }
10639 } /* for */
10640}
10641#else
10642#define RETURN(token) return lasttoken = token
10643static int
10644xxreadtoken(void)
10645{
10646 int c;
10647
10648 if (tokpushback) {
10649 tokpushback = 0;
10650 return lasttoken;
10651 }
10652 if (needprompt) {
10653 setprompt(2);
10654 }
10655 startlinno = plinno;
10656 for (;;) { /* until token or start of word found */
10657 c = pgetc_macro();
10658 switch (c) {
10659 case ' ': case '\t':
10660#if ENABLE_ASH_ALIAS
10661 case PEOA:
10662#endif
10663 continue;
10664 case '#':
10665 while ((c = pgetc()) != '\n' && c != PEOF);
10666 pungetc();
10667 continue;
10668 case '\\':
10669 if (pgetc() == '\n') {
10670 startlinno = ++plinno;
10671 if (doprompt)
10672 setprompt(2);
10673 continue;
10674 }
10675 pungetc();
10676 goto breakloop;
10677 case '\n':
10678 plinno++;
10679 needprompt = doprompt;
10680 RETURN(TNL);
10681 case PEOF:
10682 RETURN(TEOF);
10683 case '&':
10684 if (pgetc() == '&')
10685 RETURN(TAND);
10686 pungetc();
10687 RETURN(TBACKGND);
10688 case '|':
10689 if (pgetc() == '|')
10690 RETURN(TOR);
10691 pungetc();
10692 RETURN(TPIPE);
10693 case ';':
10694 if (pgetc() == ';')
10695 RETURN(TENDCASE);
10696 pungetc();
10697 RETURN(TSEMI);
10698 case '(':
10699 RETURN(TLP);
10700 case ')':
10701 RETURN(TRP);
10702 default:
10703 goto breakloop;
10704 }
10705 }
10706 breakloop:
10707 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10708#undef RETURN
10709}
10710#endif /* NEW_xxreadtoken */
10711
10712static int
10713readtoken(void)
10714{
10715 int t;
10716#if DEBUG
10717 int alreadyseen = tokpushback;
10718#endif
10719
10720#if ENABLE_ASH_ALIAS
10721 top:
10722#endif
10723
10724 t = xxreadtoken();
10725
10726 /*
10727 * eat newlines
10728 */
10729 if (checkkwd & CHKNL) {
10730 while (t == TNL) {
10731 parseheredoc();
10732 t = xxreadtoken();
10733 }
10734 }
10735
10736 if (t != TWORD || quoteflag) {
10737 goto out;
10738 }
10739
10740 /*
10741 * check for keywords
10742 */
10743 if (checkkwd & CHKKWD) {
10744 const char *const *pp;
10745
10746 pp = findkwd(wordtext);
10747 if (pp) {
10748 lasttoken = t = pp - tokname_array;
10749 TRACE(("keyword %s recognized\n", tokname(t)));
10750 goto out;
10751 }
10752 }
10753
10754 if (checkkwd & CHKALIAS) {
10755#if ENABLE_ASH_ALIAS
10756 struct alias *ap;
10757 ap = lookupalias(wordtext, 1);
10758 if (ap != NULL) {
10759 if (*ap->val) {
10760 pushstring(ap->val, ap);
10761 }
10762 goto top;
10763 }
10764#endif
10765 }
10766 out:
10767 checkkwd = 0;
10768#if DEBUG
10769 if (!alreadyseen)
10770 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10771 else
10772 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10773#endif
10774 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010775}
10776
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010777static char
10778peektoken(void)
10779{
10780 int t;
10781
10782 t = readtoken();
10783 tokpushback++;
10784 return tokname_array[t][0];
10785}
Eric Andersencb57d552001-06-28 07:25:16 +000010786
10787/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010788 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10789 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010790 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010791static union node *
10792parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010793{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010794 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010795
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010796 tokpushback = 0;
10797 doprompt = interact;
10798 if (doprompt)
10799 setprompt(doprompt);
10800 needprompt = 0;
10801 t = readtoken();
10802 if (t == TEOF)
10803 return NEOF;
10804 if (t == TNL)
10805 return NULL;
10806 tokpushback++;
10807 return list(1);
10808}
10809
10810/*
10811 * Input any here documents.
10812 */
10813static void
10814parseheredoc(void)
10815{
10816 struct heredoc *here;
10817 union node *n;
10818
10819 here = heredoclist;
10820 heredoclist = 0;
10821
10822 while (here) {
10823 if (needprompt) {
10824 setprompt(2);
10825 }
10826 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10827 here->eofmark, here->striptabs);
10828 n = stalloc(sizeof(struct narg));
10829 n->narg.type = NARG;
10830 n->narg.next = NULL;
10831 n->narg.text = wordtext;
10832 n->narg.backquote = backquotelist;
10833 here->here->nhere.doc = n;
10834 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010835 }
Eric Andersencb57d552001-06-28 07:25:16 +000010836}
10837
10838
10839/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010840 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010841 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010842#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010843static const char *
10844expandstr(const char *ps)
10845{
10846 union node n;
10847
10848 /* XXX Fix (char *) cast. */
10849 setinputstring((char *)ps);
10850 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10851 popfile();
10852
10853 n.narg.type = NARG;
10854 n.narg.next = NULL;
10855 n.narg.text = wordtext;
10856 n.narg.backquote = backquotelist;
10857
10858 expandarg(&n, NULL, 0);
10859 return stackblock();
10860}
10861#endif
10862
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010863/*
10864 * Execute a command or commands contained in a string.
10865 */
10866static int
10867evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010868{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010869 union node *n;
10870 struct stackmark smark;
10871 int skip;
10872
10873 setinputstring(s);
10874 setstackmark(&smark);
10875
10876 skip = 0;
10877 while ((n = parsecmd(0)) != NEOF) {
10878 evaltree(n, 0);
10879 popstackmark(&smark);
10880 skip = evalskip;
10881 if (skip)
10882 break;
10883 }
10884 popfile();
10885
10886 skip &= mask;
10887 evalskip = skip;
10888 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010889}
10890
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010891/*
10892 * The eval command.
10893 */
10894static int
10895evalcmd(int argc, char **argv)
10896{
10897 char *p;
10898 char *concat;
10899 char **ap;
10900
10901 if (argc > 1) {
10902 p = argv[1];
10903 if (argc > 2) {
10904 STARTSTACKSTR(concat);
10905 ap = argv + 2;
10906 for (;;) {
10907 concat = stack_putstr(p, concat);
10908 p = *ap++;
10909 if (p == NULL)
10910 break;
10911 STPUTC(' ', concat);
10912 }
10913 STPUTC('\0', concat);
10914 p = grabstackstr(concat);
10915 }
10916 evalstring(p, ~SKIPEVAL);
10917
10918 }
10919 return exitstatus;
10920}
10921
10922/*
10923 * Read and execute commands. "Top" is nonzero for the top level command
10924 * loop; it turns on prompting if the shell is interactive.
10925 */
10926static int
10927cmdloop(int top)
10928{
10929 union node *n;
10930 struct stackmark smark;
10931 int inter;
10932 int numeof = 0;
10933
10934 TRACE(("cmdloop(%d) called\n", top));
10935 for (;;) {
10936 int skip;
10937
10938 setstackmark(&smark);
10939#if JOBS
10940 if (jobctl)
10941 showjobs(stderr, SHOW_CHANGED);
10942#endif
10943 inter = 0;
10944 if (iflag && top) {
10945 inter++;
10946#if ENABLE_ASH_MAIL
10947 chkmail();
10948#endif
10949 }
10950 n = parsecmd(inter);
10951 /* showtree(n); DEBUG */
10952 if (n == NEOF) {
10953 if (!top || numeof >= 50)
10954 break;
10955 if (!stoppedjobs()) {
10956 if (!Iflag)
10957 break;
10958 out2str("\nUse \"exit\" to leave shell.\n");
10959 }
10960 numeof++;
10961 } else if (nflag == 0) {
10962 job_warning = (job_warning == 2) ? 1 : 0;
10963 numeof = 0;
10964 evaltree(n, 0);
10965 }
10966 popstackmark(&smark);
10967 skip = evalskip;
10968
10969 if (skip) {
10970 evalskip = 0;
10971 return skip & SKIPEVAL;
10972 }
10973 }
10974 return 0;
10975}
10976
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010977/*
10978 * Take commands from a file. To be compatible we should do a path
10979 * search for the file, which is necessary to find sub-commands.
10980 */
10981static char *
10982find_dot_file(char *name)
10983{
10984 char *fullname;
10985 const char *path = pathval();
10986 struct stat statb;
10987
10988 /* don't try this for absolute or relative paths */
10989 if (strchr(name, '/'))
10990 return name;
10991
10992 while ((fullname = padvance(&path, name)) != NULL) {
10993 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10994 /*
10995 * Don't bother freeing here, since it will
10996 * be freed by the caller.
10997 */
10998 return fullname;
10999 }
11000 stunalloc(fullname);
11001 }
11002
11003 /* not found in the PATH */
11004 ash_msg_and_raise_error("%s: not found", name);
11005 /* NOTREACHED */
11006}
11007
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011008static int
11009dotcmd(int argc, char **argv)
11010{
11011 struct strlist *sp;
11012 volatile struct shparam saveparam;
11013 int status = 0;
11014
11015 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011016 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011017
11018 if (argc >= 2) { /* That's what SVR2 does */
11019 char *fullname;
11020
11021 fullname = find_dot_file(argv[1]);
11022
11023 if (argc > 2) {
11024 saveparam = shellparam;
11025 shellparam.malloc = 0;
11026 shellparam.nparam = argc - 2;
11027 shellparam.p = argv + 2;
11028 };
11029
11030 setinputfile(fullname, INPUT_PUSH_FILE);
11031 commandname = fullname;
11032 cmdloop(0);
11033 popfile();
11034
11035 if (argc > 2) {
11036 freeparam(&shellparam);
11037 shellparam = saveparam;
11038 };
11039 status = exitstatus;
11040 }
11041 return status;
11042}
11043
11044static int
11045exitcmd(int argc, char **argv)
11046{
11047 if (stoppedjobs())
11048 return 0;
11049 if (argc > 1)
11050 exitstatus = number(argv[1]);
11051 raise_exception(EXEXIT);
11052 /* NOTREACHED */
11053}
11054
11055#if ENABLE_ASH_BUILTIN_ECHO
11056static int
11057echocmd(int argc, char **argv)
11058{
11059 return bb_echo(argv);
11060}
11061#endif
11062
11063#if ENABLE_ASH_BUILTIN_TEST
11064static int
11065testcmd(int argc, char **argv)
11066{
11067 return bb_test(argc, argv);
11068}
11069#endif
11070
11071/*
11072 * Read a file containing shell functions.
11073 */
11074static void
11075readcmdfile(char *name)
11076{
11077 setinputfile(name, INPUT_PUSH_FILE);
11078 cmdloop(0);
11079 popfile();
11080}
11081
11082
Denis Vlasenkocc571512007-02-23 21:10:35 +000011083/* ============ find_command inplementation */
11084
11085/*
11086 * Resolve a command name. If you change this routine, you may have to
11087 * change the shellexec routine as well.
11088 */
11089static void
11090find_command(char *name, struct cmdentry *entry, int act, const char *path)
11091{
11092 struct tblentry *cmdp;
11093 int idx;
11094 int prev;
11095 char *fullname;
11096 struct stat statb;
11097 int e;
11098 int updatetbl;
11099 struct builtincmd *bcmd;
11100
11101 /* If name contains a slash, don't use PATH or hash table */
11102 if (strchr(name, '/') != NULL) {
11103 entry->u.index = -1;
11104 if (act & DO_ABS) {
11105 while (stat(name, &statb) < 0) {
11106#ifdef SYSV
11107 if (errno == EINTR)
11108 continue;
11109#endif
11110 entry->cmdtype = CMDUNKNOWN;
11111 return;
11112 }
11113 }
11114 entry->cmdtype = CMDNORMAL;
11115 return;
11116 }
11117
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011118#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenkocc571512007-02-23 21:10:35 +000011119 if (find_applet_by_name(name)) {
11120 entry->cmdtype = CMDNORMAL;
11121 entry->u.index = -1;
11122 return;
11123 }
Denis Vlasenko29e31dd2007-03-03 23:12:17 +000011124#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000011125
11126 updatetbl = (path == pathval());
11127 if (!updatetbl) {
11128 act |= DO_ALTPATH;
11129 if (strstr(path, "%builtin") != NULL)
11130 act |= DO_ALTBLTIN;
11131 }
11132
11133 /* If name is in the table, check answer will be ok */
11134 cmdp = cmdlookup(name, 0);
11135 if (cmdp != NULL) {
11136 int bit;
11137
11138 switch (cmdp->cmdtype) {
11139 default:
11140#if DEBUG
11141 abort();
11142#endif
11143 case CMDNORMAL:
11144 bit = DO_ALTPATH;
11145 break;
11146 case CMDFUNCTION:
11147 bit = DO_NOFUNC;
11148 break;
11149 case CMDBUILTIN:
11150 bit = DO_ALTBLTIN;
11151 break;
11152 }
11153 if (act & bit) {
11154 updatetbl = 0;
11155 cmdp = NULL;
11156 } else if (cmdp->rehash == 0)
11157 /* if not invalidated by cd, we're done */
11158 goto success;
11159 }
11160
11161 /* If %builtin not in path, check for builtin next */
11162 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011163 if (bcmd) {
11164 if (IS_BUILTIN_REGULAR(bcmd))
11165 goto builtin_success;
11166 if (act & DO_ALTPATH) {
11167 if (!(act & DO_ALTBLTIN))
11168 goto builtin_success;
11169 } else if (builtinloc <= 0) {
11170 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011171 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011172 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011173
11174 /* We have to search path. */
11175 prev = -1; /* where to start */
11176 if (cmdp && cmdp->rehash) { /* doing a rehash */
11177 if (cmdp->cmdtype == CMDBUILTIN)
11178 prev = builtinloc;
11179 else
11180 prev = cmdp->param.index;
11181 }
11182
11183 e = ENOENT;
11184 idx = -1;
11185 loop:
11186 while ((fullname = padvance(&path, name)) != NULL) {
11187 stunalloc(fullname);
11188 idx++;
11189 if (pathopt) {
11190 if (prefix(pathopt, "builtin")) {
11191 if (bcmd)
11192 goto builtin_success;
11193 continue;
11194 } else if (!(act & DO_NOFUNC) &&
11195 prefix(pathopt, "func")) {
11196 /* handled below */
11197 } else {
11198 /* ignore unimplemented options */
11199 continue;
11200 }
11201 }
11202 /* if rehash, don't redo absolute path names */
11203 if (fullname[0] == '/' && idx <= prev) {
11204 if (idx < prev)
11205 continue;
11206 TRACE(("searchexec \"%s\": no change\n", name));
11207 goto success;
11208 }
11209 while (stat(fullname, &statb) < 0) {
11210#ifdef SYSV
11211 if (errno == EINTR)
11212 continue;
11213#endif
11214 if (errno != ENOENT && errno != ENOTDIR)
11215 e = errno;
11216 goto loop;
11217 }
11218 e = EACCES; /* if we fail, this will be the error */
11219 if (!S_ISREG(statb.st_mode))
11220 continue;
11221 if (pathopt) { /* this is a %func directory */
11222 stalloc(strlen(fullname) + 1);
11223 readcmdfile(fullname);
11224 cmdp = cmdlookup(name, 0);
11225 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11226 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11227 stunalloc(fullname);
11228 goto success;
11229 }
11230 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11231 if (!updatetbl) {
11232 entry->cmdtype = CMDNORMAL;
11233 entry->u.index = idx;
11234 return;
11235 }
11236 INT_OFF;
11237 cmdp = cmdlookup(name, 1);
11238 cmdp->cmdtype = CMDNORMAL;
11239 cmdp->param.index = idx;
11240 INT_ON;
11241 goto success;
11242 }
11243
11244 /* We failed. If there was an entry for this command, delete it */
11245 if (cmdp && updatetbl)
11246 delete_cmd_entry();
11247 if (act & DO_ERR)
11248 ash_msg("%s: %s", name, errmsg(e, "not found"));
11249 entry->cmdtype = CMDUNKNOWN;
11250 return;
11251
11252 builtin_success:
11253 if (!updatetbl) {
11254 entry->cmdtype = CMDBUILTIN;
11255 entry->u.cmd = bcmd;
11256 return;
11257 }
11258 INT_OFF;
11259 cmdp = cmdlookup(name, 1);
11260 cmdp->cmdtype = CMDBUILTIN;
11261 cmdp->param.cmd = bcmd;
11262 INT_ON;
11263 success:
11264 cmdp->rehash = 0;
11265 entry->cmdtype = cmdp->cmdtype;
11266 entry->u = cmdp->param;
11267}
11268
11269
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011270/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011271
Eric Andersencb57d552001-06-28 07:25:16 +000011272/*
Eric Andersencb57d552001-06-28 07:25:16 +000011273 * The trap builtin.
11274 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011275static int
Eric Andersenc470f442003-07-28 09:56:35 +000011276trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011277{
11278 char *action;
11279 char **ap;
11280 int signo;
11281
Eric Andersenc470f442003-07-28 09:56:35 +000011282 nextopt(nullstr);
11283 ap = argptr;
11284 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011285 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011286 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011287 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011288
Rob Landleyc9c1a412006-07-12 19:17:55 +000011289 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011290 out1fmt("trap -- %s %s\n",
11291 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011292 }
11293 }
11294 return 0;
11295 }
Eric Andersenc470f442003-07-28 09:56:35 +000011296 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011297 action = NULL;
11298 else
11299 action = *ap++;
11300 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011301 signo = get_signum(*ap);
11302 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011303 ash_msg_and_raise_error("%s: bad trap", *ap);
11304 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011305 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011306 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011307 action = NULL;
11308 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011309 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011310 }
Eric Andersenc470f442003-07-28 09:56:35 +000011311 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011312 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011313 trap[signo] = action;
11314 if (signo != 0)
11315 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011316 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011317 ap++;
11318 }
11319 return 0;
11320}
11321
Eric Andersenc470f442003-07-28 09:56:35 +000011322
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011323/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011324
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011325#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011326/*
11327 * Lists available builtins
11328 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011329static int
11330helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011331{
11332 int col, i;
11333
11334 out1fmt("\nBuilt-in commands:\n-------------------\n");
11335 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11336 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011337 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011338 if (col > 60) {
11339 out1fmt("\n");
11340 col = 0;
11341 }
11342 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011343#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011344 for (i = 0; i < NUM_APPLETS; i++) {
11345 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11346 if (col > 60) {
11347 out1fmt("\n");
11348 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011349 }
11350 }
11351#endif
11352 out1fmt("\n\n");
11353 return EXIT_SUCCESS;
11354}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011355#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011356
Eric Andersencb57d552001-06-28 07:25:16 +000011357/*
Eric Andersencb57d552001-06-28 07:25:16 +000011358 * The export and readonly commands.
11359 */
Eric Andersenc470f442003-07-28 09:56:35 +000011360static int
11361exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011362{
11363 struct var *vp;
11364 char *name;
11365 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011366 char **aptr;
11367 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011368
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011369 if (nextopt("p") != 'p') {
11370 aptr = argptr;
11371 name = *aptr;
11372 if (name) {
11373 do {
11374 p = strchr(name, '=');
11375 if (p != NULL) {
11376 p++;
11377 } else {
11378 vp = *findvar(hashvar(name), name);
11379 if (vp) {
11380 vp->flags |= flag;
11381 continue;
11382 }
Eric Andersencb57d552001-06-28 07:25:16 +000011383 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011384 setvar(name, p, flag);
11385 } while ((name = *++aptr) != NULL);
11386 return 0;
11387 }
Eric Andersencb57d552001-06-28 07:25:16 +000011388 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011389 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011390 return 0;
11391}
11392
Eric Andersencb57d552001-06-28 07:25:16 +000011393/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011394 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011395 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011396static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011397unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011398{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011399 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011400
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011401 cmdp = cmdlookup(name, 0);
11402 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11403 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011404}
11405
Eric Andersencb57d552001-06-28 07:25:16 +000011406/*
Eric Andersencb57d552001-06-28 07:25:16 +000011407 * The unset builtin command. We unset the function before we unset the
11408 * variable to allow a function to be unset when there is a readonly variable
11409 * with the same name.
11410 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011411static int
Eric Andersenc470f442003-07-28 09:56:35 +000011412unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011413{
11414 char **ap;
11415 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011416 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011417 int ret = 0;
11418
11419 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011420 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011421 }
Eric Andersencb57d552001-06-28 07:25:16 +000011422
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011423 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011424 if (flag != 'f') {
11425 i = unsetvar(*ap);
11426 ret |= i;
11427 if (!(i & 2))
11428 continue;
11429 }
11430 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011431 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011432 }
Eric Andersenc470f442003-07-28 09:56:35 +000011433 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011434}
11435
11436
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011437/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011438
Eric Andersenc470f442003-07-28 09:56:35 +000011439#include <sys/times.h>
11440
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011441static const unsigned char timescmd_str[] = {
11442 ' ', offsetof(struct tms, tms_utime),
11443 '\n', offsetof(struct tms, tms_stime),
11444 ' ', offsetof(struct tms, tms_cutime),
11445 '\n', offsetof(struct tms, tms_cstime),
11446 0
11447};
Eric Andersencb57d552001-06-28 07:25:16 +000011448
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011449static int
11450timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011451{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011452 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011453 const unsigned char *p;
11454 struct tms buf;
11455
11456 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011457 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011458
11459 p = timescmd_str;
11460 do {
11461 t = *(clock_t *)(((char *) &buf) + p[1]);
11462 s = t / clk_tck;
11463 out1fmt("%ldm%ld.%.3lds%c",
11464 s/60, s%60,
11465 ((t - s * clk_tck) * 1000) / clk_tck,
11466 p[0]);
11467 } while (*(p += 2));
11468
Eric Andersencb57d552001-06-28 07:25:16 +000011469 return 0;
11470}
11471
Denis Vlasenko131ae172007-02-18 13:00:19 +000011472#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011473static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011474dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011475{
Eric Andersened9ecf72004-06-22 08:29:45 +000011476 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011477 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011478
Denis Vlasenkob012b102007-02-19 22:43:01 +000011479 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011480 result = arith(s, &errcode);
11481 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011482 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011483 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011484 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011485 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011486 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011487 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011488 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011489 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011490 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011491
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011492 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011493}
Eric Andersenc470f442003-07-28 09:56:35 +000011494
Eric Andersenc470f442003-07-28 09:56:35 +000011495/*
Eric Andersen90898442003-08-06 11:20:52 +000011496 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11497 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11498 *
11499 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011500 */
11501static int
Eric Andersen90898442003-08-06 11:20:52 +000011502letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011503{
Eric Andersenc470f442003-07-28 09:56:35 +000011504 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011505 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011506
Eric Andersen90898442003-08-06 11:20:52 +000011507 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011508 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011509 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011510 for (ap = argv + 1; *ap; ap++) {
11511 i = dash_arith(*ap);
11512 }
Eric Andersenc470f442003-07-28 09:56:35 +000011513
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011514 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011515}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011516#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011517
Eric Andersenc470f442003-07-28 09:56:35 +000011518
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011519/* ============ miscbltin.c
11520 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011521 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011522 */
11523
11524#undef rflag
11525
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011526#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011527typedef enum __rlimit_resource rlim_t;
11528#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011529
Eric Andersenc470f442003-07-28 09:56:35 +000011530/*
11531 * The read builtin. The -e option causes backslashes to escape the
11532 * following character.
11533 *
11534 * This uses unbuffered input, which may be avoidable in some cases.
11535 */
Eric Andersenc470f442003-07-28 09:56:35 +000011536static int
11537readcmd(int argc, char **argv)
11538{
11539 char **ap;
11540 int backslash;
11541 char c;
11542 int rflag;
11543 char *prompt;
11544 const char *ifs;
11545 char *p;
11546 int startword;
11547 int status;
11548 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011549#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011550 int nch_flag = 0;
11551 int nchars = 0;
11552 int silent = 0;
11553 struct termios tty, old_tty;
11554#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011555#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011556 fd_set set;
11557 struct timeval ts;
11558
11559 ts.tv_sec = ts.tv_usec = 0;
11560#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011561
11562 rflag = 0;
11563 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011564#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011565 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011566#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011567 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011568#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011569 while ((i = nextopt("p:rt:")) != '\0')
11570#else
11571 while ((i = nextopt("p:r")) != '\0')
11572#endif
11573 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011574 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011575 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011576 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011577 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011578#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011579 case 'n':
11580 nchars = strtol(optionarg, &p, 10);
11581 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011582 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011583 nch_flag = (nchars > 0);
11584 break;
11585 case 's':
11586 silent = 1;
11587 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011588#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011589#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011590 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011591 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011592 ts.tv_usec = 0;
11593 if (*p == '.') {
11594 char *p2;
11595 if (*++p) {
11596 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011597 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011598 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011599 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011600 scale = p2 - p;
11601 /* normalize to usec */
11602 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011603 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011604 while (scale++ < 6)
11605 ts.tv_usec *= 10;
11606 }
11607 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011608 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011609 }
11610 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011611 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011612 break;
11613#endif
11614 case 'r':
11615 rflag = 1;
11616 break;
11617 default:
11618 break;
11619 }
Eric Andersenc470f442003-07-28 09:56:35 +000011620 }
11621 if (prompt && isatty(0)) {
11622 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011623 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011624 ap = argptr;
11625 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011626 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011627 ifs = bltinlookup("IFS");
11628 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011629 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011630#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011631 if (nch_flag || silent) {
11632 tcgetattr(0, &tty);
11633 old_tty = tty;
11634 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011635 tty.c_lflag &= ~ICANON;
11636 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011637 }
11638 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011639 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011640
11641 }
11642 tcsetattr(0, TCSANOW, &tty);
11643 }
11644#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011645#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011646 if (ts.tv_sec || ts.tv_usec) {
11647 FD_ZERO (&set);
11648 FD_SET (0, &set);
11649
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011650 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011651 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011652#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011653 if (nch_flag)
11654 tcsetattr(0, TCSANOW, &old_tty);
11655#endif
11656 return 1;
11657 }
11658 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011659#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011660 status = 0;
11661 startword = 1;
11662 backslash = 0;
11663 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011664#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011665 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011666#else
11667 for (;;)
11668#endif
11669 {
Eric Andersenc470f442003-07-28 09:56:35 +000011670 if (read(0, &c, 1) != 1) {
11671 status = 1;
11672 break;
11673 }
11674 if (c == '\0')
11675 continue;
11676 if (backslash) {
11677 backslash = 0;
11678 if (c != '\n')
11679 goto put;
11680 continue;
11681 }
11682 if (!rflag && c == '\\') {
11683 backslash++;
11684 continue;
11685 }
11686 if (c == '\n')
11687 break;
11688 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11689 continue;
11690 }
11691 startword = 0;
11692 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11693 STACKSTRNUL(p);
11694 setvar(*ap, stackblock(), 0);
11695 ap++;
11696 startword = 1;
11697 STARTSTACKSTR(p);
11698 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011699 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011700 STPUTC(c, p);
11701 }
11702 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011703#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011704 if (nch_flag || silent)
11705 tcsetattr(0, TCSANOW, &old_tty);
11706#endif
11707
Eric Andersenc470f442003-07-28 09:56:35 +000011708 STACKSTRNUL(p);
11709 /* Remove trailing blanks */
11710 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11711 *p = '\0';
11712 setvar(*ap, stackblock(), 0);
11713 while (*++ap != NULL)
11714 setvar(*ap, nullstr, 0);
11715 return status;
11716}
11717
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011718static int
11719umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011720{
11721 static const char permuser[3] = "ugo";
11722 static const char permmode[3] = "rwx";
11723 static const short int permmask[] = {
11724 S_IRUSR, S_IWUSR, S_IXUSR,
11725 S_IRGRP, S_IWGRP, S_IXGRP,
11726 S_IROTH, S_IWOTH, S_IXOTH
11727 };
11728
11729 char *ap;
11730 mode_t mask;
11731 int i;
11732 int symbolic_mode = 0;
11733
11734 while (nextopt("S") != '\0') {
11735 symbolic_mode = 1;
11736 }
11737
Denis Vlasenkob012b102007-02-19 22:43:01 +000011738 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011739 mask = umask(0);
11740 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011741 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011742
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011743 ap = *argptr;
11744 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011745 if (symbolic_mode) {
11746 char buf[18];
11747 char *p = buf;
11748
11749 for (i = 0; i < 3; i++) {
11750 int j;
11751
11752 *p++ = permuser[i];
11753 *p++ = '=';
11754 for (j = 0; j < 3; j++) {
11755 if ((mask & permmask[3 * i + j]) == 0) {
11756 *p++ = permmode[j];
11757 }
11758 }
11759 *p++ = ',';
11760 }
11761 *--p = 0;
11762 puts(buf);
11763 } else {
11764 out1fmt("%.4o\n", mask);
11765 }
11766 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011767 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011768 mask = 0;
11769 do {
11770 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011771 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011772 mask = (mask << 3) + (*ap - '0');
11773 } while (*++ap != '\0');
11774 umask(mask);
11775 } else {
11776 mask = ~mask & 0777;
11777 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011778 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011779 }
11780 umask(~mask & 0777);
11781 }
11782 }
11783 return 0;
11784}
11785
11786/*
11787 * ulimit builtin
11788 *
11789 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11790 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11791 * ash by J.T. Conklin.
11792 *
11793 * Public domain.
11794 */
11795
11796struct limits {
11797 const char *name;
11798 int cmd;
11799 int factor; /* multiply by to get rlim_{cur,max} values */
11800 char option;
11801};
11802
11803static const struct limits limits[] = {
11804#ifdef RLIMIT_CPU
11805 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11806#endif
11807#ifdef RLIMIT_FSIZE
11808 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11809#endif
11810#ifdef RLIMIT_DATA
11811 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11812#endif
11813#ifdef RLIMIT_STACK
11814 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11815#endif
11816#ifdef RLIMIT_CORE
11817 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11818#endif
11819#ifdef RLIMIT_RSS
11820 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11821#endif
11822#ifdef RLIMIT_MEMLOCK
11823 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11824#endif
11825#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011826 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011827#endif
11828#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011829 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011830#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011831#ifdef RLIMIT_AS
11832 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011833#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011834#ifdef RLIMIT_LOCKS
11835 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011836#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011837 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011838};
11839
Glenn L McGrath76620622004-01-13 10:19:37 +000011840enum limtype { SOFT = 0x1, HARD = 0x2 };
11841
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011842static void
11843printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011844 const struct limits *l)
11845{
11846 rlim_t val;
11847
11848 val = limit->rlim_max;
11849 if (how & SOFT)
11850 val = limit->rlim_cur;
11851
11852 if (val == RLIM_INFINITY)
11853 out1fmt("unlimited\n");
11854 else {
11855 val /= l->factor;
11856 out1fmt("%lld\n", (long long) val);
11857 }
11858}
11859
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011860static int
Eric Andersenc470f442003-07-28 09:56:35 +000011861ulimitcmd(int argc, char **argv)
11862{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011863 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011864 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011865 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011866 const struct limits *l;
11867 int set, all = 0;
11868 int optc, what;
11869 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011870
11871 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011872 while ((optc = nextopt("HSa"
11873#ifdef RLIMIT_CPU
11874 "t"
11875#endif
11876#ifdef RLIMIT_FSIZE
11877 "f"
11878#endif
11879#ifdef RLIMIT_DATA
11880 "d"
11881#endif
11882#ifdef RLIMIT_STACK
11883 "s"
11884#endif
11885#ifdef RLIMIT_CORE
11886 "c"
11887#endif
11888#ifdef RLIMIT_RSS
11889 "m"
11890#endif
11891#ifdef RLIMIT_MEMLOCK
11892 "l"
11893#endif
11894#ifdef RLIMIT_NPROC
11895 "p"
11896#endif
11897#ifdef RLIMIT_NOFILE
11898 "n"
11899#endif
11900#ifdef RLIMIT_AS
11901 "v"
11902#endif
11903#ifdef RLIMIT_LOCKS
11904 "w"
11905#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011906 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011907 switch (optc) {
11908 case 'H':
11909 how = HARD;
11910 break;
11911 case 'S':
11912 how = SOFT;
11913 break;
11914 case 'a':
11915 all = 1;
11916 break;
11917 default:
11918 what = optc;
11919 }
11920
Glenn L McGrath76620622004-01-13 10:19:37 +000011921 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011922 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011923
11924 set = *argptr ? 1 : 0;
11925 if (set) {
11926 char *p = *argptr;
11927
11928 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011929 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011930 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011931 val = RLIM_INFINITY;
11932 else {
11933 val = (rlim_t) 0;
11934
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011935 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011936 val = (val * 10) + (long)(c - '0');
11937 if (val < (rlim_t) 0)
11938 break;
11939 }
11940 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011941 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011942 val *= l->factor;
11943 }
11944 }
11945 if (all) {
11946 for (l = limits; l->name; l++) {
11947 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011948 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011949 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011950 }
11951 return 0;
11952 }
11953
11954 getrlimit(l->cmd, &limit);
11955 if (set) {
11956 if (how & HARD)
11957 limit.rlim_max = val;
11958 if (how & SOFT)
11959 limit.rlim_cur = val;
11960 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011961 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011962 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011963 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011964 }
11965 return 0;
11966}
11967
Eric Andersen90898442003-08-06 11:20:52 +000011968
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011969/* ============ Math support */
11970
Denis Vlasenko131ae172007-02-18 13:00:19 +000011971#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011972
11973/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11974
11975 Permission is hereby granted, free of charge, to any person obtaining
11976 a copy of this software and associated documentation files (the
11977 "Software"), to deal in the Software without restriction, including
11978 without limitation the rights to use, copy, modify, merge, publish,
11979 distribute, sublicense, and/or sell copies of the Software, and to
11980 permit persons to whom the Software is furnished to do so, subject to
11981 the following conditions:
11982
11983 The above copyright notice and this permission notice shall be
11984 included in all copies or substantial portions of the Software.
11985
11986 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11987 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11988 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11989 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11990 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11991 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11992 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11993*/
11994
11995/* This is my infix parser/evaluator. It is optimized for size, intended
11996 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011997 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011998 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000011999 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012000 * be that which POSIX specifies for shells. */
12001
12002/* The code uses a simple two-stack algorithm. See
12003 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012004 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012005 * this is based (this code differs in that it applies operators immediately
12006 * to the stack instead of adding them to a queue to end up with an
12007 * expression). */
12008
12009/* To use the routine, call it with an expression string and error return
12010 * pointer */
12011
12012/*
12013 * Aug 24, 2001 Manuel Novoa III
12014 *
12015 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12016 *
12017 * 1) In arith_apply():
12018 * a) Cached values of *numptr and &(numptr[-1]).
12019 * b) Removed redundant test for zero denominator.
12020 *
12021 * 2) In arith():
12022 * a) Eliminated redundant code for processing operator tokens by moving
12023 * to a table-based implementation. Also folded handling of parens
12024 * into the table.
12025 * b) Combined all 3 loops which called arith_apply to reduce generated
12026 * code size at the cost of speed.
12027 *
12028 * 3) The following expressions were treated as valid by the original code:
12029 * 1() , 0! , 1 ( *3 ) .
12030 * These bugs have been fixed by internally enclosing the expression in
12031 * parens and then checking that all binary ops and right parens are
12032 * preceded by a valid expression (NUM_TOKEN).
12033 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012034 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012035 * ctype's isspace() if it is used by another busybox applet or if additional
12036 * whitespace chars should be considered. Look below the "#include"s for a
12037 * precompiler test.
12038 */
12039
12040/*
12041 * Aug 26, 2001 Manuel Novoa III
12042 *
12043 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12044 *
12045 * Merge in Aaron's comments previously posted to the busybox list,
12046 * modified slightly to take account of my changes to the code.
12047 *
12048 */
12049
12050/*
12051 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12052 *
12053 * - allow access to variable,
12054 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12055 * - realize assign syntax (VAR=expr, +=, *= etc)
12056 * - realize exponentiation (** operator)
12057 * - realize comma separated - expr, expr
12058 * - realise ++expr --expr expr++ expr--
12059 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012060 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012061 * - was restored loses XOR operator
12062 * - remove one goto label, added three ;-)
12063 * - protect $((num num)) as true zero expr (Manuel`s error)
12064 * - always use special isspace(), see comment from bash ;-)
12065 */
12066
Eric Andersen90898442003-08-06 11:20:52 +000012067#define arith_isspace(arithval) \
12068 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12069
Eric Andersen90898442003-08-06 11:20:52 +000012070typedef unsigned char operator;
12071
12072/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012073 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012074 * precedence. The ID portion is so that multiple operators can have the
12075 * same precedence, ensuring that the leftmost one is evaluated first.
12076 * Consider * and /. */
12077
12078#define tok_decl(prec,id) (((id)<<5)|(prec))
12079#define PREC(op) ((op) & 0x1F)
12080
12081#define TOK_LPAREN tok_decl(0,0)
12082
12083#define TOK_COMMA tok_decl(1,0)
12084
12085#define TOK_ASSIGN tok_decl(2,0)
12086#define TOK_AND_ASSIGN tok_decl(2,1)
12087#define TOK_OR_ASSIGN tok_decl(2,2)
12088#define TOK_XOR_ASSIGN tok_decl(2,3)
12089#define TOK_PLUS_ASSIGN tok_decl(2,4)
12090#define TOK_MINUS_ASSIGN tok_decl(2,5)
12091#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12092#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12093
12094#define TOK_MUL_ASSIGN tok_decl(3,0)
12095#define TOK_DIV_ASSIGN tok_decl(3,1)
12096#define TOK_REM_ASSIGN tok_decl(3,2)
12097
12098/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012099#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012100
12101/* conditional is right associativity too */
12102#define TOK_CONDITIONAL tok_decl(4,0)
12103#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12104
12105#define TOK_OR tok_decl(5,0)
12106
12107#define TOK_AND tok_decl(6,0)
12108
12109#define TOK_BOR tok_decl(7,0)
12110
12111#define TOK_BXOR tok_decl(8,0)
12112
12113#define TOK_BAND tok_decl(9,0)
12114
12115#define TOK_EQ tok_decl(10,0)
12116#define TOK_NE tok_decl(10,1)
12117
12118#define TOK_LT tok_decl(11,0)
12119#define TOK_GT tok_decl(11,1)
12120#define TOK_GE tok_decl(11,2)
12121#define TOK_LE tok_decl(11,3)
12122
12123#define TOK_LSHIFT tok_decl(12,0)
12124#define TOK_RSHIFT tok_decl(12,1)
12125
12126#define TOK_ADD tok_decl(13,0)
12127#define TOK_SUB tok_decl(13,1)
12128
12129#define TOK_MUL tok_decl(14,0)
12130#define TOK_DIV tok_decl(14,1)
12131#define TOK_REM tok_decl(14,2)
12132
12133/* exponent is right associativity */
12134#define TOK_EXPONENT tok_decl(15,1)
12135
12136/* For now unary operators. */
12137#define UNARYPREC 16
12138#define TOK_BNOT tok_decl(UNARYPREC,0)
12139#define TOK_NOT tok_decl(UNARYPREC,1)
12140
12141#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12142#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12143
12144#define PREC_PRE (UNARYPREC+2)
12145
12146#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12147#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12148
12149#define PREC_POST (UNARYPREC+3)
12150
12151#define TOK_POST_INC tok_decl(PREC_POST, 0)
12152#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12153
12154#define SPEC_PREC (UNARYPREC+4)
12155
12156#define TOK_NUM tok_decl(SPEC_PREC, 0)
12157#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12158
12159#define NUMPTR (*numstackptr)
12160
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012161static int
12162tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012163{
12164 operator prec = PREC(op);
12165
12166 convert_prec_is_assing(prec);
12167 return (prec == PREC(TOK_ASSIGN) ||
12168 prec == PREC_PRE || prec == PREC_POST);
12169}
12170
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012171static int
12172is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012173{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012174 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12175 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012176}
12177
Eric Andersen90898442003-08-06 11:20:52 +000012178typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012179 arith_t val;
12180 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012181 char contidional_second_val_initialized;
12182 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012183 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012184} v_n_t;
12185
Eric Andersen90898442003-08-06 11:20:52 +000012186typedef struct CHK_VAR_RECURSIVE_LOOPED {
12187 const char *var;
12188 struct CHK_VAR_RECURSIVE_LOOPED *next;
12189} chk_var_recursive_looped_t;
12190
12191static chk_var_recursive_looped_t *prev_chk_var_recursive;
12192
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012193static int
12194arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012195{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012196 if (t->var) {
12197 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012198
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012199 if (p) {
12200 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012201
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012202 /* recursive try as expression */
12203 chk_var_recursive_looped_t *cur;
12204 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012205
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012206 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12207 if (strcmp(cur->var, t->var) == 0) {
12208 /* expression recursion loop detected */
12209 return -5;
12210 }
12211 }
12212 /* save current lookuped var name */
12213 cur = prev_chk_var_recursive;
12214 cur_save.var = t->var;
12215 cur_save.next = cur;
12216 prev_chk_var_recursive = &cur_save;
12217
12218 t->val = arith (p, &errcode);
12219 /* restore previous ptr after recursiving */
12220 prev_chk_var_recursive = cur;
12221 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012222 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012223 /* allow undefined var as 0 */
12224 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012225 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012226 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012227}
12228
12229/* "applying" a token means performing it on the top elements on the integer
12230 * stack. For a unary operator it will only change the top element, but a
12231 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012232static int
12233arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012234{
Eric Andersen90898442003-08-06 11:20:52 +000012235 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012236 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012237 int ret_arith_lookup_val;
12238
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012239 /* There is no operator that can work without arguments */
12240 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012241 numptr_m1 = NUMPTR - 1;
12242
12243 /* check operand is var with noninteger value */
12244 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012245 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012246 return ret_arith_lookup_val;
12247
12248 rez = numptr_m1->val;
12249 if (op == TOK_UMINUS)
12250 rez *= -1;
12251 else if (op == TOK_NOT)
12252 rez = !rez;
12253 else if (op == TOK_BNOT)
12254 rez = ~rez;
12255 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12256 rez++;
12257 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12258 rez--;
12259 else if (op != TOK_UPLUS) {
12260 /* Binary operators */
12261
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012262 /* check and binary operators need two arguments */
12263 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012264
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012265 /* ... and they pop one */
12266 --NUMPTR;
12267 numptr_val = rez;
12268 if (op == TOK_CONDITIONAL) {
12269 if (! numptr_m1->contidional_second_val_initialized) {
12270 /* protect $((expr1 ? expr2)) without ": expr" */
12271 goto err;
12272 }
12273 rez = numptr_m1->contidional_second_val;
12274 } else if (numptr_m1->contidional_second_val_initialized) {
12275 /* protect $((expr1 : expr2)) without "expr ? " */
12276 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012277 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012278 numptr_m1 = NUMPTR - 1;
12279 if (op != TOK_ASSIGN) {
12280 /* check operand is var with noninteger value for not '=' */
12281 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12282 if (ret_arith_lookup_val)
12283 return ret_arith_lookup_val;
12284 }
12285 if (op == TOK_CONDITIONAL) {
12286 numptr_m1->contidional_second_val = rez;
12287 }
12288 rez = numptr_m1->val;
12289 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012290 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012291 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012292 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012293 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012294 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012295 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012296 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012297 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012298 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012299 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012300 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012301 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012302 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012303 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012304 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012305 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012306 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012307 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012308 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012309 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012310 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012311 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012312 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012313 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012314 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012315 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012316 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012317 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012318 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012319 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012320 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012321 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012322 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012323 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012324 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012325 /* protect $((expr : expr)) without "expr ? " */
12326 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012327 }
12328 numptr_m1->contidional_second_val_initialized = op;
12329 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012330 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012331 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012332 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012333 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012334 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012335 return -3; /* exponent less than 0 */
12336 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012337 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012338
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012339 if (numptr_val)
12340 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012341 c *= rez;
12342 rez = c;
12343 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012344 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012345 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012346 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012347 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012348 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012349 rez %= numptr_val;
12350 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012351 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012352 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012353
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012354 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012355 /* Hmm, 1=2 ? */
12356 goto err;
12357 }
12358 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012359#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012360 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012361#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012362 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012363#endif
Eric Andersen90898442003-08-06 11:20:52 +000012364 setvar(numptr_m1->var, buf, 0);
12365 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012366 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012367 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012368 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012369 rez++;
12370 }
12371 numptr_m1->val = rez;
12372 /* protect geting var value, is number now */
12373 numptr_m1->var = NULL;
12374 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012375 err:
12376 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012377}
12378
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012379/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012380static const char op_tokens[] = {
12381 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12382 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12383 '<','<', 0, TOK_LSHIFT,
12384 '>','>', 0, TOK_RSHIFT,
12385 '|','|', 0, TOK_OR,
12386 '&','&', 0, TOK_AND,
12387 '!','=', 0, TOK_NE,
12388 '<','=', 0, TOK_LE,
12389 '>','=', 0, TOK_GE,
12390 '=','=', 0, TOK_EQ,
12391 '|','=', 0, TOK_OR_ASSIGN,
12392 '&','=', 0, TOK_AND_ASSIGN,
12393 '*','=', 0, TOK_MUL_ASSIGN,
12394 '/','=', 0, TOK_DIV_ASSIGN,
12395 '%','=', 0, TOK_REM_ASSIGN,
12396 '+','=', 0, TOK_PLUS_ASSIGN,
12397 '-','=', 0, TOK_MINUS_ASSIGN,
12398 '-','-', 0, TOK_POST_DEC,
12399 '^','=', 0, TOK_XOR_ASSIGN,
12400 '+','+', 0, TOK_POST_INC,
12401 '*','*', 0, TOK_EXPONENT,
12402 '!', 0, TOK_NOT,
12403 '<', 0, TOK_LT,
12404 '>', 0, TOK_GT,
12405 '=', 0, TOK_ASSIGN,
12406 '|', 0, TOK_BOR,
12407 '&', 0, TOK_BAND,
12408 '*', 0, TOK_MUL,
12409 '/', 0, TOK_DIV,
12410 '%', 0, TOK_REM,
12411 '+', 0, TOK_ADD,
12412 '-', 0, TOK_SUB,
12413 '^', 0, TOK_BXOR,
12414 /* uniq */
12415 '~', 0, TOK_BNOT,
12416 ',', 0, TOK_COMMA,
12417 '?', 0, TOK_CONDITIONAL,
12418 ':', 0, TOK_CONDITIONAL_SEP,
12419 ')', 0, TOK_RPAREN,
12420 '(', 0, TOK_LPAREN,
12421 0
12422};
12423/* ptr to ")" */
12424#define endexpression &op_tokens[sizeof(op_tokens)-7]
12425
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012426static arith_t
12427arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012428{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012429 char arithval; /* Current character under analysis */
12430 operator lasttok, op;
12431 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012432
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012433 const char *p = endexpression;
12434 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012435
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012436 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012437
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012438 /* Stack of integers */
12439 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12440 * in any given correct or incorrect expression is left as an exercise to
12441 * the reader. */
12442 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12443 *numstackptr = numstack;
12444 /* Stack of operator tokens */
12445 operator *stack = alloca((datasizes) * sizeof(operator)),
12446 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012447
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012448 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12449 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012450
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012451 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012452 arithval = *expr;
12453 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012454 if (p == endexpression) {
12455 /* Null expression. */
12456 return 0;
12457 }
12458
12459 /* This is only reached after all tokens have been extracted from the
12460 * input stream. If there are still tokens on the operator stack, they
12461 * are to be applied in order. At the end, there should be a final
12462 * result on the integer stack */
12463
12464 if (expr != endexpression + 1) {
12465 /* If we haven't done so already, */
12466 /* append a closing right paren */
12467 expr = endexpression;
12468 /* and let the loop process it. */
12469 continue;
12470 }
12471 /* At this point, we're done with the expression. */
12472 if (numstackptr != numstack+1) {
12473 /* ... but if there isn't, it's bad */
12474 err:
12475 return (*perrcode = -1);
12476 }
12477 if (numstack->var) {
12478 /* expression is $((var)) only, lookup now */
12479 errcode = arith_lookup_val(numstack);
12480 }
12481 ret:
12482 *perrcode = errcode;
12483 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012484 }
12485
Eric Andersen90898442003-08-06 11:20:52 +000012486 /* Continue processing the expression. */
12487 if (arith_isspace(arithval)) {
12488 /* Skip whitespace */
12489 goto prologue;
12490 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012491 p = endofname(expr);
12492 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012493 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012494
12495 numstackptr->var = alloca(var_name_size);
12496 safe_strncpy(numstackptr->var, expr, var_name_size);
12497 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012498 num:
Eric Andersen90898442003-08-06 11:20:52 +000012499 numstackptr->contidional_second_val_initialized = 0;
12500 numstackptr++;
12501 lasttok = TOK_NUM;
12502 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012503 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012504 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012505 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012506#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012507 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012508#else
12509 numstackptr->val = strtol(expr, (char **) &expr, 0);
12510#endif
Eric Andersen90898442003-08-06 11:20:52 +000012511 goto num;
12512 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012513 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012514 const char *o;
12515
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012516 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012517 /* strange operator not found */
12518 goto err;
12519 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012520 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012521 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012522 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012523 /* found */
12524 expr = o - 1;
12525 break;
12526 }
12527 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012528 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012529 p++;
12530 /* skip zero delim */
12531 p++;
12532 }
12533 op = p[1];
12534
12535 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012536 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12537 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012538
12539 /* Plus and minus are binary (not unary) _only_ if the last
12540 * token was as number, or a right paren (which pretends to be
12541 * a number, since it evaluates to one). Think about it.
12542 * It makes sense. */
12543 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012544 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012545 case TOK_ADD:
12546 op = TOK_UPLUS;
12547 break;
12548 case TOK_SUB:
12549 op = TOK_UMINUS;
12550 break;
12551 case TOK_POST_INC:
12552 op = TOK_PRE_INC;
12553 break;
12554 case TOK_POST_DEC:
12555 op = TOK_PRE_DEC;
12556 break;
Eric Andersen90898442003-08-06 11:20:52 +000012557 }
12558 }
12559 /* We don't want a unary operator to cause recursive descent on the
12560 * stack, because there can be many in a row and it could cause an
12561 * operator to be evaluated before its argument is pushed onto the
12562 * integer stack. */
12563 /* But for binary operators, "apply" everything on the operator
12564 * stack until we find an operator with a lesser priority than the
12565 * one we have just extracted. */
12566 /* Left paren is given the lowest priority so it will never be
12567 * "applied" in this way.
12568 * if associativity is right and priority eq, applied also skip
12569 */
12570 prec = PREC(op);
12571 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12572 /* not left paren or unary */
12573 if (lasttok != TOK_NUM) {
12574 /* binary op must be preceded by a num */
12575 goto err;
12576 }
12577 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012578 if (op == TOK_RPAREN) {
12579 /* The algorithm employed here is simple: while we don't
12580 * hit an open paren nor the bottom of the stack, pop
12581 * tokens and apply them */
12582 if (stackptr[-1] == TOK_LPAREN) {
12583 --stackptr;
12584 /* Any operator directly after a */
12585 lasttok = TOK_NUM;
12586 /* close paren should consider itself binary */
12587 goto prologue;
12588 }
12589 } else {
12590 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012591
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012592 convert_prec_is_assing(prec);
12593 convert_prec_is_assing(prev_prec);
12594 if (prev_prec < prec)
12595 break;
12596 /* check right assoc */
12597 if (prev_prec == prec && is_right_associativity(prec))
12598 break;
12599 }
12600 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12601 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012602 }
12603 if (op == TOK_RPAREN) {
12604 goto err;
12605 }
12606 }
12607
12608 /* Push this operator to the stack and remember it. */
12609 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012610 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012611 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012612 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012613}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012614#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012615
12616
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012617/* ============ main() and helpers */
12618
12619/*
12620 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012621 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012622static void exitshell(void) ATTRIBUTE_NORETURN;
12623static void
12624exitshell(void)
12625{
12626 struct jmploc loc;
12627 char *p;
12628 int status;
12629
12630 status = exitstatus;
12631 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12632 if (setjmp(loc.loc)) {
12633 if (exception == EXEXIT)
12634/* dash bug: it just does _exit(exitstatus) here
12635 * but we have to do setjobctl(0) first!
12636 * (bug is still not fixed in dash-0.5.3 - if you run dash
12637 * under Midnight Commander, on exit from dash MC is backgrounded) */
12638 status = exitstatus;
12639 goto out;
12640 }
12641 exception_handler = &loc;
12642 p = trap[0];
12643 if (p) {
12644 trap[0] = NULL;
12645 evalstring(p, 0);
12646 }
12647 flush_stdout_stderr();
12648 out:
12649 setjobctl(0);
12650 _exit(status);
12651 /* NOTREACHED */
12652}
12653
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012654static void
12655init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012656{
12657 /* from input.c: */
12658 basepf.nextc = basepf.buf = basebuf;
12659
12660 /* from trap.c: */
12661 signal(SIGCHLD, SIG_DFL);
12662
12663 /* from var.c: */
12664 {
12665 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012666 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012667 const char *p;
12668 struct stat st1, st2;
12669
12670 initvar();
12671 for (envp = environ; envp && *envp; envp++) {
12672 if (strchr(*envp, '=')) {
12673 setvareq(*envp, VEXPORT|VTEXTFIXED);
12674 }
12675 }
12676
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012677 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012678 setvar("PPID", ppid, 0);
12679
12680 p = lookupvar("PWD");
12681 if (p)
12682 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12683 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12684 p = '\0';
12685 setpwd(p, 0);
12686 }
12687}
12688
12689/*
12690 * Process the shell command line arguments.
12691 */
12692static void
12693procargs(int argc, char **argv)
12694{
12695 int i;
12696 const char *xminusc;
12697 char **xargv;
12698
12699 xargv = argv;
12700 arg0 = xargv[0];
12701 if (argc > 0)
12702 xargv++;
12703 for (i = 0; i < NOPTS; i++)
12704 optlist[i] = 2;
12705 argptr = xargv;
12706 options(1);
12707 xargv = argptr;
12708 xminusc = minusc;
12709 if (*xargv == NULL) {
12710 if (xminusc)
12711 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12712 sflag = 1;
12713 }
12714 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12715 iflag = 1;
12716 if (mflag == 2)
12717 mflag = iflag;
12718 for (i = 0; i < NOPTS; i++)
12719 if (optlist[i] == 2)
12720 optlist[i] = 0;
12721#if DEBUG == 2
12722 debug = 1;
12723#endif
12724 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12725 if (xminusc) {
12726 minusc = *xargv++;
12727 if (*xargv)
12728 goto setarg0;
12729 } else if (!sflag) {
12730 setinputfile(*xargv, 0);
12731 setarg0:
12732 arg0 = *xargv++;
12733 commandname = arg0;
12734 }
12735
12736 shellparam.p = xargv;
12737#if ENABLE_ASH_GETOPTS
12738 shellparam.optind = 1;
12739 shellparam.optoff = -1;
12740#endif
12741 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12742 while (*xargv) {
12743 shellparam.nparam++;
12744 xargv++;
12745 }
12746 optschanged();
12747}
12748
12749/*
12750 * Read /etc/profile or .profile.
12751 */
12752static void
12753read_profile(const char *name)
12754{
12755 int skip;
12756
12757 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12758 return;
12759 skip = cmdloop(0);
12760 popfile();
12761 if (skip)
12762 exitshell();
12763}
12764
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012765/*
12766 * This routine is called when an error or an interrupt occurs in an
12767 * interactive shell and control is returned to the main command loop.
12768 */
12769static void
12770reset(void)
12771{
12772 /* from eval.c: */
12773 evalskip = 0;
12774 loopnest = 0;
12775 /* from input.c: */
12776 parselleft = parsenleft = 0; /* clear input buffer */
12777 popallfiles();
12778 /* from parser.c: */
12779 tokpushback = 0;
12780 checkkwd = 0;
12781 /* from redir.c: */
12782 clearredir(0);
12783}
12784
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012785#if PROFILE
12786static short profile_buf[16384];
12787extern int etext();
12788#endif
12789
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012790/*
12791 * Main routine. We initialize things, parse the arguments, execute
12792 * profiles if we're a login shell, and then call cmdloop to execute
12793 * commands. The setjmp call sets up the location to jump to when an
12794 * exception occurs. When an exception occurs the variable "state"
12795 * is used to figure out how far we had gotten.
12796 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012797int ash_main(int argc, char **argv);
12798int ash_main(int argc, char **argv)
12799{
12800 char *shinit;
12801 volatile int state;
12802 struct jmploc jmploc;
12803 struct stackmark smark;
12804
12805#ifdef __GLIBC__
12806 dash_errno = __errno_location();
12807#endif
12808
12809#if PROFILE
12810 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12811#endif
12812
12813#if ENABLE_FEATURE_EDITING
12814 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12815#endif
12816 state = 0;
12817 if (setjmp(jmploc.loc)) {
12818 int e;
12819 int s;
12820
12821 reset();
12822
12823 e = exception;
12824 if (e == EXERROR)
12825 exitstatus = 2;
12826 s = state;
12827 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12828 exitshell();
12829
12830 if (e == EXINT) {
12831 outcslow('\n', stderr);
12832 }
12833 popstackmark(&smark);
12834 FORCE_INT_ON; /* enable interrupts */
12835 if (s == 1)
12836 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012837 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012838 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012839 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012840 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012841 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012842 }
12843 exception_handler = &jmploc;
12844#if DEBUG
12845 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012846 trace_puts("Shell args: ");
12847 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012848#endif
12849 rootpid = getpid();
12850
12851#if ENABLE_ASH_RANDOM_SUPPORT
12852 rseed = rootpid + time(NULL);
12853#endif
12854 init();
12855 setstackmark(&smark);
12856 procargs(argc, argv);
12857#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12858 if (iflag) {
12859 const char *hp = lookupvar("HISTFILE");
12860
12861 if (hp == NULL) {
12862 hp = lookupvar("HOME");
12863 if (hp != NULL) {
12864 char *defhp = concat_path_file(hp, ".ash_history");
12865 setvar("HISTFILE", defhp, 0);
12866 free(defhp);
12867 }
12868 }
12869 }
12870#endif
12871 if (argv[0] && argv[0][0] == '-')
12872 isloginsh = 1;
12873 if (isloginsh) {
12874 state = 1;
12875 read_profile("/etc/profile");
12876 state1:
12877 state = 2;
12878 read_profile(".profile");
12879 }
12880 state2:
12881 state = 3;
12882 if (
12883#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012884 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012885#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012886 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012887 ) {
12888 shinit = lookupvar("ENV");
12889 if (shinit != NULL && *shinit != '\0') {
12890 read_profile(shinit);
12891 }
12892 }
12893 state3:
12894 state = 4;
12895 if (minusc)
12896 evalstring(minusc, 0);
12897
12898 if (sflag || minusc == NULL) {
12899#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12900 if ( iflag ) {
12901 const char *hp = lookupvar("HISTFILE");
12902
12903 if (hp != NULL)
12904 line_input_state->hist_file = hp;
12905 }
12906#endif
12907 state4: /* XXX ??? - why isn't this before the "if" statement */
12908 cmdloop(1);
12909 }
12910#if PROFILE
12911 monitor(0);
12912#endif
12913#ifdef GPROF
12914 {
12915 extern void _mcleanup(void);
12916 _mcleanup();
12917 }
12918#endif
12919 exitshell();
12920 /* NOTREACHED */
12921}
12922
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012923#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012924const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012925int main(int argc, char **argv)
12926{
12927 return ash_main(argc, argv);
12928}
12929#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012930
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012931
Eric Andersendf82f612001-06-28 07:46:40 +000012932/*-
12933 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012934 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012935 *
12936 * This code is derived from software contributed to Berkeley by
12937 * Kenneth Almquist.
12938 *
12939 * Redistribution and use in source and binary forms, with or without
12940 * modification, are permitted provided that the following conditions
12941 * are met:
12942 * 1. Redistributions of source code must retain the above copyright
12943 * notice, this list of conditions and the following disclaimer.
12944 * 2. Redistributions in binary form must reproduce the above copyright
12945 * notice, this list of conditions and the following disclaimer in the
12946 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012947 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012948 * may be used to endorse or promote products derived from this software
12949 * without specific prior written permission.
12950 *
12951 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12952 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12953 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12954 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12955 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12956 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12957 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12958 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12959 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12960 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12961 * SUCH DAMAGE.
12962 */