blob: b4278424a052813fb9f04e94cab30a4f883f9aed [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;
2296 } else if (p[1] == '\0')
2297 break;
2298 /* fall through */
2299 default:
2300 new = stack_putstr(p, new);
2301 USTPUTC('/', new);
2302 }
2303 p = strtok(0, "/");
2304 }
2305 if (new > lim)
2306 STUNPUTC(new);
2307 *new = 0;
2308 return stackblock();
2309}
2310
2311/*
2312 * Find out what the current directory is. If we already know the current
2313 * directory, this routine returns immediately.
2314 */
2315static char *
2316getpwd(void)
2317{
2318 char *dir = getcwd(0, 0);
2319 return dir ? dir : nullstr;
2320}
2321
2322static void
2323setpwd(const char *val, int setold)
2324{
2325 char *oldcur, *dir;
2326
2327 oldcur = dir = curdir;
2328
2329 if (setold) {
2330 setvar("OLDPWD", oldcur, VEXPORT);
2331 }
2332 INT_OFF;
2333 if (physdir != nullstr) {
2334 if (physdir != oldcur)
2335 free(physdir);
2336 physdir = nullstr;
2337 }
2338 if (oldcur == val || !val) {
2339 char *s = getpwd();
2340 physdir = s;
2341 if (!val)
2342 dir = s;
2343 } else
2344 dir = ckstrdup(val);
2345 if (oldcur != dir && oldcur != nullstr) {
2346 free(oldcur);
2347 }
2348 curdir = dir;
2349 INT_ON;
2350 setvar("PWD", dir, VEXPORT);
2351}
2352
2353static void hashcd(void);
2354
2355/*
2356 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2357 * know that the current directory has changed.
2358 */
2359static int
2360docd(const char *dest, int flags)
2361{
2362 const char *dir = 0;
2363 int err;
2364
2365 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2366
2367 INT_OFF;
2368 if (!(flags & CD_PHYSICAL)) {
2369 dir = updatepwd(dest);
2370 if (dir)
2371 dest = dir;
2372 }
2373 err = chdir(dest);
2374 if (err)
2375 goto out;
2376 setpwd(dir, 1);
2377 hashcd();
2378 out:
2379 INT_ON;
2380 return err;
2381}
2382
2383static int
2384cdcmd(int argc, char **argv)
2385{
2386 const char *dest;
2387 const char *path;
2388 const char *p;
2389 char c;
2390 struct stat statb;
2391 int flags;
2392
2393 flags = cdopt();
2394 dest = *argptr;
2395 if (!dest)
2396 dest = bltinlookup(homestr);
2397 else if (LONE_DASH(dest)) {
2398 dest = bltinlookup("OLDPWD");
2399 flags |= CD_PRINT;
2400 }
2401 if (!dest)
2402 dest = nullstr;
2403 if (*dest == '/')
2404 goto step7;
2405 if (*dest == '.') {
2406 c = dest[1];
2407 dotdot:
2408 switch (c) {
2409 case '\0':
2410 case '/':
2411 goto step6;
2412 case '.':
2413 c = dest[2];
2414 if (c != '.')
2415 goto dotdot;
2416 }
2417 }
2418 if (!*dest)
2419 dest = ".";
2420 path = bltinlookup("CDPATH");
2421 if (!path) {
2422 step6:
2423 step7:
2424 p = dest;
2425 goto docd;
2426 }
2427 do {
2428 c = *path;
2429 p = padvance(&path, dest);
2430 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2431 if (c && c != ':')
2432 flags |= CD_PRINT;
2433 docd:
2434 if (!docd(p, flags))
2435 goto out;
2436 break;
2437 }
2438 } while (path);
2439 ash_msg_and_raise_error("can't cd to %s", dest);
2440 /* NOTREACHED */
2441 out:
2442 if (flags & CD_PRINT)
2443 out1fmt(snlfmt, curdir);
2444 return 0;
2445}
2446
2447static int
2448pwdcmd(int argc, char **argv)
2449{
2450 int flags;
2451 const char *dir = curdir;
2452
2453 flags = cdopt();
2454 if (flags) {
2455 if (physdir == nullstr)
2456 setpwd(dir, 0);
2457 dir = physdir;
2458 }
2459 out1fmt(snlfmt, dir);
2460 return 0;
2461}
2462
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002463
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002464/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002465
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002466#define IBUFSIZ (BUFSIZ + 1)
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002467#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002468
Eric Andersenc470f442003-07-28 09:56:35 +00002469/* Syntax classes */
2470#define CWORD 0 /* character is nothing special */
2471#define CNL 1 /* newline character */
2472#define CBACK 2 /* a backslash character */
2473#define CSQUOTE 3 /* single quote */
2474#define CDQUOTE 4 /* double quote */
2475#define CENDQUOTE 5 /* a terminating quote */
2476#define CBQUOTE 6 /* backwards single quote */
2477#define CVAR 7 /* a dollar sign */
2478#define CENDVAR 8 /* a '}' character */
2479#define CLP 9 /* a left paren in arithmetic */
2480#define CRP 10 /* a right paren in arithmetic */
2481#define CENDFILE 11 /* end of file */
2482#define CCTL 12 /* like CWORD, except it must be escaped */
2483#define CSPCL 13 /* these terminate a word */
2484#define CIGN 14 /* character should be ignored */
2485
Denis Vlasenko131ae172007-02-18 13:00:19 +00002486#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002487#define SYNBASE 130
2488#define PEOF -130
2489#define PEOA -129
2490#define PEOA_OR_PEOF PEOA
2491#else
2492#define SYNBASE 129
2493#define PEOF -129
2494#define PEOA_OR_PEOF PEOF
2495#endif
2496
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002497/* number syntax index */
2498#define BASESYNTAX 0 /* not in quotes */
2499#define DQSYNTAX 1 /* in double quotes */
2500#define SQSYNTAX 2 /* in single quotes */
2501#define ARISYNTAX 3 /* in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002502
Denis Vlasenko131ae172007-02-18 13:00:19 +00002503#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002504#define USE_SIT_FUNCTION
2505#endif
2506
Denis Vlasenko131ae172007-02-18 13:00:19 +00002507#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002508static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002509#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002510 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002511#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002512 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2513 { CNL, CNL, CNL, CNL }, /* 2, \n */
2514 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2515 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2516 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2517 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2518 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2519 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2520 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2521 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2522 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002523#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002524 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2525 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2526 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002527#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002528};
Eric Andersenc470f442003-07-28 09:56:35 +00002529#else
2530static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002531#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002532 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002533#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002534 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2535 { CNL, CNL, CNL }, /* 2, \n */
2536 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2537 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2538 { CVAR, CVAR, CWORD }, /* 5, $ */
2539 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2540 { CSPCL, CWORD, CWORD }, /* 7, ( */
2541 { CSPCL, CWORD, CWORD }, /* 8, ) */
2542 { CBACK, CBACK, CCTL }, /* 9, \ */
2543 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2544 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002545#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002546 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2547 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2548 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002549#endif
2550};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002551#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002552
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002553#ifdef USE_SIT_FUNCTION
2554
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002555static int
2556SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002557{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002558 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002559#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002560 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002561 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2562 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2563 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2564 11, 3 /* "}~" */
2565 };
2566#else
2567 static const char syntax_index_table[] = {
2568 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2569 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2570 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2571 10, 2 /* "}~" */
2572 };
2573#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002574 const char *s;
2575 int indx;
2576
Eric Andersenc470f442003-07-28 09:56:35 +00002577 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002578 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002579#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002580 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002581 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002582 else
2583#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002584#define U_C(c) ((unsigned char)(c))
2585
2586 if ((unsigned char)c >= (unsigned char)(CTLESC)
2587 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2588 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002589 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002590 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002591 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002592 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002593 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002594 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002595 }
2596 return S_I_T[indx][syntax];
2597}
2598
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002599#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002600
Denis Vlasenko131ae172007-02-18 13:00:19 +00002601#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002602#define CSPCL_CIGN_CIGN_CIGN 0
2603#define CSPCL_CWORD_CWORD_CWORD 1
2604#define CNL_CNL_CNL_CNL 2
2605#define CWORD_CCTL_CCTL_CWORD 3
2606#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2607#define CVAR_CVAR_CWORD_CVAR 5
2608#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2609#define CSPCL_CWORD_CWORD_CLP 7
2610#define CSPCL_CWORD_CWORD_CRP 8
2611#define CBACK_CBACK_CCTL_CBACK 9
2612#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2613#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2614#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2615#define CWORD_CWORD_CWORD_CWORD 13
2616#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002617#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002618#define CSPCL_CWORD_CWORD_CWORD 0
2619#define CNL_CNL_CNL_CNL 1
2620#define CWORD_CCTL_CCTL_CWORD 2
2621#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2622#define CVAR_CVAR_CWORD_CVAR 4
2623#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2624#define CSPCL_CWORD_CWORD_CLP 6
2625#define CSPCL_CWORD_CWORD_CRP 7
2626#define CBACK_CBACK_CCTL_CBACK 8
2627#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2628#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2629#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2630#define CWORD_CWORD_CWORD_CWORD 12
2631#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002632#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002633
2634static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002635 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002636 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002637#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002638 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2639#endif
2640 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2641 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2642 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2643 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2644 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2645 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2646 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2647 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2648 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002649 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2650 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2778 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2779 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2801 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002802 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002803 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2804 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2805 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2806 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002807 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002808 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2809 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2810 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2811 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2812 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2813 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2814 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2815 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2816 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2817 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2818 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2819 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2820 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2821 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2822 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2823 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2824 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2825 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2826 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2827 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2828 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2829 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2830 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2831 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2832 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2833 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2851 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2852 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2853 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2854 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2855 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2856 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2857 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2858 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2859 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2860 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2861 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2862 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2863 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2865 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2866 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2893 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2894 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2895 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002896};
2897
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002898#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2899
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002900#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002901
Eric Andersen2870d962001-07-02 17:27:21 +00002902
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002903/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002904
Denis Vlasenko131ae172007-02-18 13:00:19 +00002905#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002906
2907#define ALIASINUSE 1
2908#define ALIASDEAD 2
2909
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002910#define ATABSIZE 39
2911
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002912struct alias {
2913 struct alias *next;
2914 char *name;
2915 char *val;
2916 int flag;
2917};
2918
Eric Andersen2870d962001-07-02 17:27:21 +00002919static struct alias *atab[ATABSIZE];
2920
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002921static struct alias **
2922__lookupalias(const char *name) {
2923 unsigned int hashval;
2924 struct alias **app;
2925 const char *p;
2926 unsigned int ch;
2927
2928 p = name;
2929
2930 ch = (unsigned char)*p;
2931 hashval = ch << 4;
2932 while (ch) {
2933 hashval += ch;
2934 ch = (unsigned char)*++p;
2935 }
2936 app = &atab[hashval % ATABSIZE];
2937
2938 for (; *app; app = &(*app)->next) {
2939 if (strcmp(name, (*app)->name) == 0) {
2940 break;
2941 }
2942 }
2943
2944 return app;
2945}
2946
2947static struct alias *
2948lookupalias(const char *name, int check)
2949{
2950 struct alias *ap = *__lookupalias(name);
2951
2952 if (check && ap && (ap->flag & ALIASINUSE))
2953 return NULL;
2954 return ap;
2955}
2956
2957static struct alias *
2958freealias(struct alias *ap)
2959{
2960 struct alias *next;
2961
2962 if (ap->flag & ALIASINUSE) {
2963 ap->flag |= ALIASDEAD;
2964 return ap;
2965 }
2966
2967 next = ap->next;
2968 free(ap->name);
2969 free(ap->val);
2970 free(ap);
2971 return next;
2972}
Eric Andersencb57d552001-06-28 07:25:16 +00002973
Eric Andersenc470f442003-07-28 09:56:35 +00002974static void
2975setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002976{
2977 struct alias *ap, **app;
2978
2979 app = __lookupalias(name);
2980 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002981 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002982 if (ap) {
2983 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002984 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002985 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002986 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002987 ap->flag &= ~ALIASDEAD;
2988 } else {
2989 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002990 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002991 ap->name = ckstrdup(name);
2992 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002993 ap->flag = 0;
2994 ap->next = 0;
2995 *app = ap;
2996 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002997 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002998}
2999
Eric Andersenc470f442003-07-28 09:56:35 +00003000static int
3001unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003002{
Eric Andersencb57d552001-06-28 07:25:16 +00003003 struct alias **app;
3004
3005 app = __lookupalias(name);
3006
3007 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003008 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003009 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003010 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003011 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003012 }
3013
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003014 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003015}
3016
Eric Andersenc470f442003-07-28 09:56:35 +00003017static void
3018rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003019{
Eric Andersencb57d552001-06-28 07:25:16 +00003020 struct alias *ap, **app;
3021 int i;
3022
Denis Vlasenkob012b102007-02-19 22:43:01 +00003023 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003024 for (i = 0; i < ATABSIZE; i++) {
3025 app = &atab[i];
3026 for (ap = *app; ap; ap = *app) {
3027 *app = freealias(*app);
3028 if (ap == *app) {
3029 app = &ap->next;
3030 }
3031 }
3032 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003033 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003034}
3035
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003036static void
3037printalias(const struct alias *ap)
3038{
3039 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3040}
3041
Eric Andersencb57d552001-06-28 07:25:16 +00003042/*
3043 * TODO - sort output
3044 */
Eric Andersenc470f442003-07-28 09:56:35 +00003045static int
3046aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003047{
3048 char *n, *v;
3049 int ret = 0;
3050 struct alias *ap;
3051
3052 if (argc == 1) {
3053 int i;
3054
3055 for (i = 0; i < ATABSIZE; i++)
3056 for (ap = atab[i]; ap; ap = ap->next) {
3057 printalias(ap);
3058 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003059 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003060 }
3061 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003062 v = strchr(n+1, '=');
3063 if (v == NULL) { /* n+1: funny ksh stuff */
3064 ap = *__lookupalias(n);
3065 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003066 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003067 ret = 1;
3068 } else
3069 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003070 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003071 *v++ = '\0';
3072 setalias(n, v);
3073 }
3074 }
3075
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003076 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003077}
3078
Eric Andersenc470f442003-07-28 09:56:35 +00003079static int
3080unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003081{
3082 int i;
3083
3084 while ((i = nextopt("a")) != '\0') {
3085 if (i == 'a') {
3086 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003087 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003088 }
3089 }
3090 for (i = 0; *argptr; argptr++) {
3091 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003092 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003093 i = 1;
3094 }
3095 }
3096
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003097 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003098}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003099
Denis Vlasenko131ae172007-02-18 13:00:19 +00003100#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003101
Eric Andersenc470f442003-07-28 09:56:35 +00003102
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003103/* ============ jobs.c */
3104
3105/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3106#define FORK_FG 0
3107#define FORK_BG 1
3108#define FORK_NOJOB 2
3109
3110/* mode flags for showjob(s) */
3111#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3112#define SHOW_PID 0x04 /* include process pid */
3113#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3114
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003115/*
3116 * A job structure contains information about a job. A job is either a
3117 * single process or a set of processes contained in a pipeline. In the
3118 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3119 * array of pids.
3120 */
3121
3122struct procstat {
3123 pid_t pid; /* process id */
3124 int status; /* last process status from wait() */
3125 char *cmd; /* text of command being run */
3126};
3127
3128struct job {
3129 struct procstat ps0; /* status of process */
3130 struct procstat *ps; /* status or processes when more than one */
3131#if JOBS
3132 int stopstatus; /* status of a stopped job */
3133#endif
3134 uint32_t
3135 nprocs: 16, /* number of processes */
3136 state: 8,
3137#define JOBRUNNING 0 /* at least one proc running */
3138#define JOBSTOPPED 1 /* all procs are stopped */
3139#define JOBDONE 2 /* all procs are completed */
3140#if JOBS
3141 sigint: 1, /* job was killed by SIGINT */
3142 jobctl: 1, /* job running under job control */
3143#endif
3144 waited: 1, /* true if this entry has been waited for */
3145 used: 1, /* true if this entry is in used */
3146 changed: 1; /* true if status has changed */
3147 struct job *prev_job; /* previous job */
3148};
3149
3150static pid_t backgndpid; /* pid of last background process */
3151static int job_warning; /* user was warned about stopped jobs */
3152#if JOBS
3153static int jobctl; /* true if doing job control */
3154#endif
3155
3156static struct job *makejob(union node *, int);
3157static int forkshell(struct job *, union node *, int);
3158static int waitforjob(struct job *);
3159
3160#if ! JOBS
3161#define setjobctl(on) /* do nothing */
3162#else
3163static void setjobctl(int);
3164static void showjobs(FILE *, int);
3165#endif
3166
3167/*
3168 * Set the signal handler for the specified signal. The routine figures
3169 * out what it should be set to.
3170 */
3171static void
3172setsignal(int signo)
3173{
3174 int action;
3175 char *t, tsig;
3176 struct sigaction act;
3177
3178 t = trap[signo];
3179 if (t == NULL)
3180 action = S_DFL;
3181 else if (*t != '\0')
3182 action = S_CATCH;
3183 else
3184 action = S_IGN;
3185 if (rootshell && action == S_DFL) {
3186 switch (signo) {
3187 case SIGINT:
3188 if (iflag || minusc || sflag == 0)
3189 action = S_CATCH;
3190 break;
3191 case SIGQUIT:
3192#if DEBUG
3193 if (debug)
3194 break;
3195#endif
3196 /* FALLTHROUGH */
3197 case SIGTERM:
3198 if (iflag)
3199 action = S_IGN;
3200 break;
3201#if JOBS
3202 case SIGTSTP:
3203 case SIGTTOU:
3204 if (mflag)
3205 action = S_IGN;
3206 break;
3207#endif
3208 }
3209 }
3210
3211 t = &sigmode[signo - 1];
3212 tsig = *t;
3213 if (tsig == 0) {
3214 /*
3215 * current setting unknown
3216 */
3217 if (sigaction(signo, 0, &act) == -1) {
3218 /*
3219 * Pretend it worked; maybe we should give a warning
3220 * here, but other shells don't. We don't alter
3221 * sigmode, so that we retry every time.
3222 */
3223 return;
3224 }
3225 if (act.sa_handler == SIG_IGN) {
3226 if (mflag
3227 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3228 ) {
3229 tsig = S_IGN; /* don't hard ignore these */
3230 } else
3231 tsig = S_HARD_IGN;
3232 } else {
3233 tsig = S_RESET; /* force to be set */
3234 }
3235 }
3236 if (tsig == S_HARD_IGN || tsig == action)
3237 return;
3238 switch (action) {
3239 case S_CATCH:
3240 act.sa_handler = onsig;
3241 break;
3242 case S_IGN:
3243 act.sa_handler = SIG_IGN;
3244 break;
3245 default:
3246 act.sa_handler = SIG_DFL;
3247 }
3248 *t = action;
3249 act.sa_flags = 0;
3250 sigfillset(&act.sa_mask);
3251 sigaction(signo, &act, 0);
3252}
3253
3254/* mode flags for set_curjob */
3255#define CUR_DELETE 2
3256#define CUR_RUNNING 1
3257#define CUR_STOPPED 0
3258
3259/* mode flags for dowait */
3260#define DOWAIT_NORMAL 0
3261#define DOWAIT_BLOCK 1
3262
3263#if JOBS
3264/* pgrp of shell on invocation */
3265static int initialpgrp;
3266static int ttyfd = -1;
3267#endif
3268/* array of jobs */
3269static struct job *jobtab;
3270/* size of array */
3271static unsigned njobs;
3272/* current job */
3273static struct job *curjob;
3274/* number of presumed living untracked jobs */
3275static int jobless;
3276
3277static void
3278set_curjob(struct job *jp, unsigned mode)
3279{
3280 struct job *jp1;
3281 struct job **jpp, **curp;
3282
3283 /* first remove from list */
3284 jpp = curp = &curjob;
3285 do {
3286 jp1 = *jpp;
3287 if (jp1 == jp)
3288 break;
3289 jpp = &jp1->prev_job;
3290 } while (1);
3291 *jpp = jp1->prev_job;
3292
3293 /* Then re-insert in correct position */
3294 jpp = curp;
3295 switch (mode) {
3296 default:
3297#if DEBUG
3298 abort();
3299#endif
3300 case CUR_DELETE:
3301 /* job being deleted */
3302 break;
3303 case CUR_RUNNING:
3304 /* newly created job or backgrounded job,
3305 put after all stopped jobs. */
3306 do {
3307 jp1 = *jpp;
3308#if JOBS
3309 if (!jp1 || jp1->state != JOBSTOPPED)
3310#endif
3311 break;
3312 jpp = &jp1->prev_job;
3313 } while (1);
3314 /* FALLTHROUGH */
3315#if JOBS
3316 case CUR_STOPPED:
3317#endif
3318 /* newly stopped job - becomes curjob */
3319 jp->prev_job = *jpp;
3320 *jpp = jp;
3321 break;
3322 }
3323}
3324
3325#if JOBS || DEBUG
3326static int
3327jobno(const struct job *jp)
3328{
3329 return jp - jobtab + 1;
3330}
3331#endif
3332
3333/*
3334 * Convert a job name to a job structure.
3335 */
3336static struct job *
3337getjob(const char *name, int getctl)
3338{
3339 struct job *jp;
3340 struct job *found;
3341 const char *err_msg = "No such job: %s";
3342 unsigned num;
3343 int c;
3344 const char *p;
3345 char *(*match)(const char *, const char *);
3346
3347 jp = curjob;
3348 p = name;
3349 if (!p)
3350 goto currentjob;
3351
3352 if (*p != '%')
3353 goto err;
3354
3355 c = *++p;
3356 if (!c)
3357 goto currentjob;
3358
3359 if (!p[1]) {
3360 if (c == '+' || c == '%') {
3361 currentjob:
3362 err_msg = "No current job";
3363 goto check;
3364 }
3365 if (c == '-') {
3366 if (jp)
3367 jp = jp->prev_job;
3368 err_msg = "No previous job";
3369 check:
3370 if (!jp)
3371 goto err;
3372 goto gotit;
3373 }
3374 }
3375
3376 if (is_number(p)) {
3377 num = atoi(p);
3378 if (num < njobs) {
3379 jp = jobtab + num - 1;
3380 if (jp->used)
3381 goto gotit;
3382 goto err;
3383 }
3384 }
3385
3386 match = prefix;
3387 if (*p == '?') {
3388 match = strstr;
3389 p++;
3390 }
3391
3392 found = 0;
3393 while (1) {
3394 if (!jp)
3395 goto err;
3396 if (match(jp->ps[0].cmd, p)) {
3397 if (found)
3398 goto err;
3399 found = jp;
3400 err_msg = "%s: ambiguous";
3401 }
3402 jp = jp->prev_job;
3403 }
3404
3405 gotit:
3406#if JOBS
3407 err_msg = "job %s not created under job control";
3408 if (getctl && jp->jobctl == 0)
3409 goto err;
3410#endif
3411 return jp;
3412 err:
3413 ash_msg_and_raise_error(err_msg, name);
3414}
3415
3416/*
3417 * Mark a job structure as unused.
3418 */
3419static void
3420freejob(struct job *jp)
3421{
3422 struct procstat *ps;
3423 int i;
3424
3425 INT_OFF;
3426 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3427 if (ps->cmd != nullstr)
3428 free(ps->cmd);
3429 }
3430 if (jp->ps != &jp->ps0)
3431 free(jp->ps);
3432 jp->used = 0;
3433 set_curjob(jp, CUR_DELETE);
3434 INT_ON;
3435}
3436
3437#if JOBS
3438static void
3439xtcsetpgrp(int fd, pid_t pgrp)
3440{
3441 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003442 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003443}
3444
3445/*
3446 * Turn job control on and off.
3447 *
3448 * Note: This code assumes that the third arg to ioctl is a character
3449 * pointer, which is true on Berkeley systems but not System V. Since
3450 * System V doesn't have job control yet, this isn't a problem now.
3451 *
3452 * Called with interrupts off.
3453 */
3454static void
3455setjobctl(int on)
3456{
3457 int fd;
3458 int pgrp;
3459
3460 if (on == jobctl || rootshell == 0)
3461 return;
3462 if (on) {
3463 int ofd;
3464 ofd = fd = open(_PATH_TTY, O_RDWR);
3465 if (fd < 0) {
3466 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3467 * That sometimes helps to acquire controlling tty.
3468 * Obviously, a workaround for bugs when someone
3469 * failed to provide a controlling tty to bash! :) */
3470 fd += 3;
3471 while (!isatty(fd) && --fd >= 0)
3472 ;
3473 }
3474 fd = fcntl(fd, F_DUPFD, 10);
3475 close(ofd);
3476 if (fd < 0)
3477 goto out;
3478 fcntl(fd, F_SETFD, FD_CLOEXEC);
3479 do { /* while we are in the background */
3480 pgrp = tcgetpgrp(fd);
3481 if (pgrp < 0) {
3482 out:
3483 ash_msg("can't access tty; job control turned off");
3484 mflag = on = 0;
3485 goto close;
3486 }
3487 if (pgrp == getpgrp())
3488 break;
3489 killpg(0, SIGTTIN);
3490 } while (1);
3491 initialpgrp = pgrp;
3492
3493 setsignal(SIGTSTP);
3494 setsignal(SIGTTOU);
3495 setsignal(SIGTTIN);
3496 pgrp = rootpid;
3497 setpgid(0, pgrp);
3498 xtcsetpgrp(fd, pgrp);
3499 } else {
3500 /* turning job control off */
3501 fd = ttyfd;
3502 pgrp = initialpgrp;
3503 xtcsetpgrp(fd, pgrp);
3504 setpgid(0, pgrp);
3505 setsignal(SIGTSTP);
3506 setsignal(SIGTTOU);
3507 setsignal(SIGTTIN);
3508 close:
3509 close(fd);
3510 fd = -1;
3511 }
3512 ttyfd = fd;
3513 jobctl = on;
3514}
3515
3516static int
3517killcmd(int argc, char **argv)
3518{
3519 int signo = -1;
3520 int list = 0;
3521 int i;
3522 pid_t pid;
3523 struct job *jp;
3524
3525 if (argc <= 1) {
3526 usage:
3527 ash_msg_and_raise_error(
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003528"usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003529"kill -l [exitstatus]"
3530 );
3531 }
3532
3533 if (**++argv == '-') {
3534 signo = get_signum(*argv + 1);
3535 if (signo < 0) {
3536 int c;
3537
3538 while ((c = nextopt("ls:")) != '\0') {
3539 switch (c) {
3540 default:
3541#if DEBUG
3542 abort();
3543#endif
3544 case 'l':
3545 list = 1;
3546 break;
3547 case 's':
3548 signo = get_signum(optionarg);
3549 if (signo < 0) {
3550 ash_msg_and_raise_error(
3551 "invalid signal number or name: %s",
3552 optionarg
3553 );
3554 }
3555 break;
3556 }
3557 }
3558 argv = argptr;
3559 } else
3560 argv++;
3561 }
3562
3563 if (!list && signo < 0)
3564 signo = SIGTERM;
3565
3566 if ((signo < 0 || !*argv) ^ list) {
3567 goto usage;
3568 }
3569
3570 if (list) {
3571 const char *name;
3572
3573 if (!*argv) {
3574 for (i = 1; i < NSIG; i++) {
3575 name = get_signame(i);
3576 if (isdigit(*name))
3577 out1fmt(snlfmt, name);
3578 }
3579 return 0;
3580 }
3581 name = get_signame(signo);
3582 if (!isdigit(*name))
3583 ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
3584 out1fmt(snlfmt, name);
3585 return 0;
3586 }
3587
3588 i = 0;
3589 do {
3590 if (**argv == '%') {
3591 jp = getjob(*argv, 0);
3592 pid = -jp->ps[0].pid;
3593 } else {
3594 pid = **argv == '-' ?
3595 -number(*argv + 1) : number(*argv);
3596 }
3597 if (kill(pid, signo) != 0) {
3598 ash_msg("(%d) - %m", pid);
3599 i = 1;
3600 }
3601 } while (*++argv);
3602
3603 return i;
3604}
3605
3606static void
3607showpipe(struct job *jp, FILE *out)
3608{
3609 struct procstat *sp;
3610 struct procstat *spend;
3611
3612 spend = jp->ps + jp->nprocs;
3613 for (sp = jp->ps + 1; sp < spend; sp++)
3614 fprintf(out, " | %s", sp->cmd);
3615 outcslow('\n', out);
3616 flush_stdout_stderr();
3617}
3618
3619
3620static int
3621restartjob(struct job *jp, int mode)
3622{
3623 struct procstat *ps;
3624 int i;
3625 int status;
3626 pid_t pgid;
3627
3628 INT_OFF;
3629 if (jp->state == JOBDONE)
3630 goto out;
3631 jp->state = JOBRUNNING;
3632 pgid = jp->ps->pid;
3633 if (mode == FORK_FG)
3634 xtcsetpgrp(ttyfd, pgid);
3635 killpg(pgid, SIGCONT);
3636 ps = jp->ps;
3637 i = jp->nprocs;
3638 do {
3639 if (WIFSTOPPED(ps->status)) {
3640 ps->status = -1;
3641 }
3642 } while (ps++, --i);
3643 out:
3644 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3645 INT_ON;
3646 return status;
3647}
3648
3649static int
3650fg_bgcmd(int argc, char **argv)
3651{
3652 struct job *jp;
3653 FILE *out;
3654 int mode;
3655 int retval;
3656
3657 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3658 nextopt(nullstr);
3659 argv = argptr;
3660 out = stdout;
3661 do {
3662 jp = getjob(*argv, 1);
3663 if (mode == FORK_BG) {
3664 set_curjob(jp, CUR_RUNNING);
3665 fprintf(out, "[%d] ", jobno(jp));
3666 }
3667 outstr(jp->ps->cmd, out);
3668 showpipe(jp, out);
3669 retval = restartjob(jp, mode);
3670 } while (*argv && *++argv);
3671 return retval;
3672}
3673#endif
3674
3675static int
3676sprint_status(char *s, int status, int sigonly)
3677{
3678 int col;
3679 int st;
3680
3681 col = 0;
3682 if (!WIFEXITED(status)) {
3683#if JOBS
3684 if (WIFSTOPPED(status))
3685 st = WSTOPSIG(status);
3686 else
3687#endif
3688 st = WTERMSIG(status);
3689 if (sigonly) {
3690 if (st == SIGINT || st == SIGPIPE)
3691 goto out;
3692#if JOBS
3693 if (WIFSTOPPED(status))
3694 goto out;
3695#endif
3696 }
3697 st &= 0x7f;
3698 col = fmtstr(s, 32, strsignal(st));
3699 if (WCOREDUMP(status)) {
3700 col += fmtstr(s + col, 16, " (core dumped)");
3701 }
3702 } else if (!sigonly) {
3703 st = WEXITSTATUS(status);
3704 if (st)
3705 col = fmtstr(s, 16, "Done(%d)", st);
3706 else
3707 col = fmtstr(s, 16, "Done");
3708 }
3709 out:
3710 return col;
3711}
3712
3713/*
3714 * Do a wait system call. If job control is compiled in, we accept
3715 * stopped processes. If block is zero, we return a value of zero
3716 * rather than blocking.
3717 *
3718 * System V doesn't have a non-blocking wait system call. It does
3719 * have a SIGCLD signal that is sent to a process when one of it's
3720 * children dies. The obvious way to use SIGCLD would be to install
3721 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3722 * was received, and have waitproc bump another counter when it got
3723 * the status of a process. Waitproc would then know that a wait
3724 * system call would not block if the two counters were different.
3725 * This approach doesn't work because if a process has children that
3726 * have not been waited for, System V will send it a SIGCLD when it
3727 * installs a signal handler for SIGCLD. What this means is that when
3728 * a child exits, the shell will be sent SIGCLD signals continuously
3729 * until is runs out of stack space, unless it does a wait call before
3730 * restoring the signal handler. The code below takes advantage of
3731 * this (mis)feature by installing a signal handler for SIGCLD and
3732 * then checking to see whether it was called. If there are any
3733 * children to be waited for, it will be.
3734 *
3735 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3736 * waits at all. In this case, the user will not be informed when
3737 * a background process until the next time she runs a real program
3738 * (as opposed to running a builtin command or just typing return),
3739 * and the jobs command may give out of date information.
3740 */
3741static int
3742waitproc(int block, int *status)
3743{
3744 int flags = 0;
3745
3746#if JOBS
3747 if (jobctl)
3748 flags |= WUNTRACED;
3749#endif
3750 if (block == 0)
3751 flags |= WNOHANG;
3752 return wait3(status, flags, (struct rusage *)NULL);
3753}
3754
3755/*
3756 * Wait for a process to terminate.
3757 */
3758static int
3759dowait(int block, struct job *job)
3760{
3761 int pid;
3762 int status;
3763 struct job *jp;
3764 struct job *thisjob;
3765 int state;
3766
3767 TRACE(("dowait(%d) called\n", block));
3768 pid = waitproc(block, &status);
3769 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3770 if (pid <= 0)
3771 return pid;
3772 INT_OFF;
3773 thisjob = NULL;
3774 for (jp = curjob; jp; jp = jp->prev_job) {
3775 struct procstat *sp;
3776 struct procstat *spend;
3777 if (jp->state == JOBDONE)
3778 continue;
3779 state = JOBDONE;
3780 spend = jp->ps + jp->nprocs;
3781 sp = jp->ps;
3782 do {
3783 if (sp->pid == pid) {
3784 TRACE(("Job %d: changing status of proc %d "
3785 "from 0x%x to 0x%x\n",
3786 jobno(jp), pid, sp->status, status));
3787 sp->status = status;
3788 thisjob = jp;
3789 }
3790 if (sp->status == -1)
3791 state = JOBRUNNING;
3792#if JOBS
3793 if (state == JOBRUNNING)
3794 continue;
3795 if (WIFSTOPPED(sp->status)) {
3796 jp->stopstatus = sp->status;
3797 state = JOBSTOPPED;
3798 }
3799#endif
3800 } while (++sp < spend);
3801 if (thisjob)
3802 goto gotjob;
3803 }
3804#if JOBS
3805 if (!WIFSTOPPED(status))
3806#endif
3807
3808 jobless--;
3809 goto out;
3810
3811 gotjob:
3812 if (state != JOBRUNNING) {
3813 thisjob->changed = 1;
3814
3815 if (thisjob->state != state) {
3816 TRACE(("Job %d: changing state from %d to %d\n",
3817 jobno(thisjob), thisjob->state, state));
3818 thisjob->state = state;
3819#if JOBS
3820 if (state == JOBSTOPPED) {
3821 set_curjob(thisjob, CUR_STOPPED);
3822 }
3823#endif
3824 }
3825 }
3826
3827 out:
3828 INT_ON;
3829
3830 if (thisjob && thisjob == job) {
3831 char s[48 + 1];
3832 int len;
3833
3834 len = sprint_status(s, status, 1);
3835 if (len) {
3836 s[len] = '\n';
3837 s[len + 1] = 0;
3838 out2str(s);
3839 }
3840 }
3841 return pid;
3842}
3843
3844#if JOBS
3845static void
3846showjob(FILE *out, struct job *jp, int mode)
3847{
3848 struct procstat *ps;
3849 struct procstat *psend;
3850 int col;
3851 int indent;
3852 char s[80];
3853
3854 ps = jp->ps;
3855
3856 if (mode & SHOW_PGID) {
3857 /* just output process (group) id of pipeline */
3858 fprintf(out, "%d\n", ps->pid);
3859 return;
3860 }
3861
3862 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3863 indent = col;
3864
3865 if (jp == curjob)
3866 s[col - 2] = '+';
3867 else if (curjob && jp == curjob->prev_job)
3868 s[col - 2] = '-';
3869
3870 if (mode & SHOW_PID)
3871 col += fmtstr(s + col, 16, "%d ", ps->pid);
3872
3873 psend = ps + jp->nprocs;
3874
3875 if (jp->state == JOBRUNNING) {
3876 strcpy(s + col, "Running");
3877 col += sizeof("Running") - 1;
3878 } else {
3879 int status = psend[-1].status;
3880 if (jp->state == JOBSTOPPED)
3881 status = jp->stopstatus;
3882 col += sprint_status(s + col, status, 0);
3883 }
3884
3885 goto start;
3886
3887 do {
3888 /* for each process */
3889 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
3890 start:
3891 fprintf(out, "%s%*c%s",
3892 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3893 );
3894 if (!(mode & SHOW_PID)) {
3895 showpipe(jp, out);
3896 break;
3897 }
3898 if (++ps == psend) {
3899 outcslow('\n', out);
3900 break;
3901 }
3902 } while (1);
3903
3904 jp->changed = 0;
3905
3906 if (jp->state == JOBDONE) {
3907 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3908 freejob(jp);
3909 }
3910}
3911
3912static int
3913jobscmd(int argc, char **argv)
3914{
3915 int mode, m;
3916 FILE *out;
3917
3918 mode = 0;
3919 while ((m = nextopt("lp"))) {
3920 if (m == 'l')
3921 mode = SHOW_PID;
3922 else
3923 mode = SHOW_PGID;
3924 }
3925
3926 out = stdout;
3927 argv = argptr;
3928 if (*argv) {
3929 do
3930 showjob(out, getjob(*argv,0), mode);
3931 while (*++argv);
3932 } else
3933 showjobs(out, mode);
3934
3935 return 0;
3936}
3937
3938/*
3939 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3940 * statuses have changed since the last call to showjobs.
3941 */
3942static void
3943showjobs(FILE *out, int mode)
3944{
3945 struct job *jp;
3946
3947 TRACE(("showjobs(%x) called\n", mode));
3948
3949 /* If not even one one job changed, there is nothing to do */
3950 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3951 continue;
3952
3953 for (jp = curjob; jp; jp = jp->prev_job) {
3954 if (!(mode & SHOW_CHANGED) || jp->changed)
3955 showjob(out, jp, mode);
3956 }
3957}
3958#endif /* JOBS */
3959
3960static int
3961getstatus(struct job *job)
3962{
3963 int status;
3964 int retval;
3965
3966 status = job->ps[job->nprocs - 1].status;
3967 retval = WEXITSTATUS(status);
3968 if (!WIFEXITED(status)) {
3969#if JOBS
3970 retval = WSTOPSIG(status);
3971 if (!WIFSTOPPED(status))
3972#endif
3973 {
3974 /* XXX: limits number of signals */
3975 retval = WTERMSIG(status);
3976#if JOBS
3977 if (retval == SIGINT)
3978 job->sigint = 1;
3979#endif
3980 }
3981 retval += 128;
3982 }
3983 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3984 jobno(job), job->nprocs, status, retval));
3985 return retval;
3986}
3987
3988static int
3989waitcmd(int argc, char **argv)
3990{
3991 struct job *job;
3992 int retval;
3993 struct job *jp;
3994
3995 EXSIGON;
3996
3997 nextopt(nullstr);
3998 retval = 0;
3999
4000 argv = argptr;
4001 if (!*argv) {
4002 /* wait for all jobs */
4003 for (;;) {
4004 jp = curjob;
4005 while (1) {
4006 if (!jp) {
4007 /* no running procs */
4008 goto out;
4009 }
4010 if (jp->state == JOBRUNNING)
4011 break;
4012 jp->waited = 1;
4013 jp = jp->prev_job;
4014 }
4015 dowait(DOWAIT_BLOCK, 0);
4016 }
4017 }
4018
4019 retval = 127;
4020 do {
4021 if (**argv != '%') {
4022 pid_t pid = number(*argv);
4023 job = curjob;
4024 goto start;
4025 do {
4026 if (job->ps[job->nprocs - 1].pid == pid)
4027 break;
4028 job = job->prev_job;
4029 start:
4030 if (!job)
4031 goto repeat;
4032 } while (1);
4033 } else
4034 job = getjob(*argv, 0);
4035 /* loop until process terminated or stopped */
4036 while (job->state == JOBRUNNING)
4037 dowait(DOWAIT_BLOCK, 0);
4038 job->waited = 1;
4039 retval = getstatus(job);
4040 repeat:
4041 ;
4042 } while (*++argv);
4043
4044 out:
4045 return retval;
4046}
4047
4048static struct job *
4049growjobtab(void)
4050{
4051 size_t len;
4052 ptrdiff_t offset;
4053 struct job *jp, *jq;
4054
4055 len = njobs * sizeof(*jp);
4056 jq = jobtab;
4057 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4058
4059 offset = (char *)jp - (char *)jq;
4060 if (offset) {
4061 /* Relocate pointers */
4062 size_t l = len;
4063
4064 jq = (struct job *)((char *)jq + l);
4065 while (l) {
4066 l -= sizeof(*jp);
4067 jq--;
4068#define joff(p) ((struct job *)((char *)(p) + l))
4069#define jmove(p) (p) = (void *)((char *)(p) + offset)
4070 if (joff(jp)->ps == &jq->ps0)
4071 jmove(joff(jp)->ps);
4072 if (joff(jp)->prev_job)
4073 jmove(joff(jp)->prev_job);
4074 }
4075 if (curjob)
4076 jmove(curjob);
4077#undef joff
4078#undef jmove
4079 }
4080
4081 njobs += 4;
4082 jobtab = jp;
4083 jp = (struct job *)((char *)jp + len);
4084 jq = jp + 3;
4085 do {
4086 jq->used = 0;
4087 } while (--jq >= jp);
4088 return jp;
4089}
4090
4091/*
4092 * Return a new job structure.
4093 * Called with interrupts off.
4094 */
4095static struct job *
4096makejob(union node *node, int nprocs)
4097{
4098 int i;
4099 struct job *jp;
4100
4101 for (i = njobs, jp = jobtab; ; jp++) {
4102 if (--i < 0) {
4103 jp = growjobtab();
4104 break;
4105 }
4106 if (jp->used == 0)
4107 break;
4108 if (jp->state != JOBDONE || !jp->waited)
4109 continue;
4110#if JOBS
4111 if (jobctl)
4112 continue;
4113#endif
4114 freejob(jp);
4115 break;
4116 }
4117 memset(jp, 0, sizeof(*jp));
4118#if JOBS
4119 if (jobctl)
4120 jp->jobctl = 1;
4121#endif
4122 jp->prev_job = curjob;
4123 curjob = jp;
4124 jp->used = 1;
4125 jp->ps = &jp->ps0;
4126 if (nprocs > 1) {
4127 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4128 }
4129 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4130 jobno(jp)));
4131 return jp;
4132}
4133
4134#if JOBS
4135/*
4136 * Return a string identifying a command (to be printed by the
4137 * jobs command).
4138 */
4139static char *cmdnextc;
4140
4141static void
4142cmdputs(const char *s)
4143{
4144 const char *p, *str;
4145 char c, cc[2] = " ";
4146 char *nextc;
4147 int subtype = 0;
4148 int quoted = 0;
4149 static const char vstype[VSTYPE + 1][4] = {
4150 "", "}", "-", "+", "?", "=",
4151 "%", "%%", "#", "##"
4152 };
4153
4154 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4155 p = s;
4156 while ((c = *p++) != 0) {
4157 str = 0;
4158 switch (c) {
4159 case CTLESC:
4160 c = *p++;
4161 break;
4162 case CTLVAR:
4163 subtype = *p++;
4164 if ((subtype & VSTYPE) == VSLENGTH)
4165 str = "${#";
4166 else
4167 str = "${";
4168 if (!(subtype & VSQUOTE) == !(quoted & 1))
4169 goto dostr;
4170 quoted ^= 1;
4171 c = '"';
4172 break;
4173 case CTLENDVAR:
4174 str = "\"}" + !(quoted & 1);
4175 quoted >>= 1;
4176 subtype = 0;
4177 goto dostr;
4178 case CTLBACKQ:
4179 str = "$(...)";
4180 goto dostr;
4181 case CTLBACKQ+CTLQUOTE:
4182 str = "\"$(...)\"";
4183 goto dostr;
4184#if ENABLE_ASH_MATH_SUPPORT
4185 case CTLARI:
4186 str = "$((";
4187 goto dostr;
4188 case CTLENDARI:
4189 str = "))";
4190 goto dostr;
4191#endif
4192 case CTLQUOTEMARK:
4193 quoted ^= 1;
4194 c = '"';
4195 break;
4196 case '=':
4197 if (subtype == 0)
4198 break;
4199 if ((subtype & VSTYPE) != VSNORMAL)
4200 quoted <<= 1;
4201 str = vstype[subtype & VSTYPE];
4202 if (subtype & VSNUL)
4203 c = ':';
4204 else
4205 goto checkstr;
4206 break;
4207 case '\'':
4208 case '\\':
4209 case '"':
4210 case '$':
4211 /* These can only happen inside quotes */
4212 cc[0] = c;
4213 str = cc;
4214 c = '\\';
4215 break;
4216 default:
4217 break;
4218 }
4219 USTPUTC(c, nextc);
4220 checkstr:
4221 if (!str)
4222 continue;
4223 dostr:
4224 while ((c = *str++)) {
4225 USTPUTC(c, nextc);
4226 }
4227 }
4228 if (quoted & 1) {
4229 USTPUTC('"', nextc);
4230 }
4231 *nextc = 0;
4232 cmdnextc = nextc;
4233}
4234
4235/* cmdtxt() and cmdlist() call each other */
4236static void cmdtxt(union node *n);
4237
4238static void
4239cmdlist(union node *np, int sep)
4240{
4241 for (; np; np = np->narg.next) {
4242 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004243 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004244 cmdtxt(np);
4245 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004246 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004247 }
4248}
4249
4250static void
4251cmdtxt(union node *n)
4252{
4253 union node *np;
4254 struct nodelist *lp;
4255 const char *p;
4256 char s[2];
4257
4258 if (!n)
4259 return;
4260 switch (n->type) {
4261 default:
4262#if DEBUG
4263 abort();
4264#endif
4265 case NPIPE:
4266 lp = n->npipe.cmdlist;
4267 for (;;) {
4268 cmdtxt(lp->n);
4269 lp = lp->next;
4270 if (!lp)
4271 break;
4272 cmdputs(" | ");
4273 }
4274 break;
4275 case NSEMI:
4276 p = "; ";
4277 goto binop;
4278 case NAND:
4279 p = " && ";
4280 goto binop;
4281 case NOR:
4282 p = " || ";
4283 binop:
4284 cmdtxt(n->nbinary.ch1);
4285 cmdputs(p);
4286 n = n->nbinary.ch2;
4287 goto donode;
4288 case NREDIR:
4289 case NBACKGND:
4290 n = n->nredir.n;
4291 goto donode;
4292 case NNOT:
4293 cmdputs("!");
4294 n = n->nnot.com;
4295 donode:
4296 cmdtxt(n);
4297 break;
4298 case NIF:
4299 cmdputs("if ");
4300 cmdtxt(n->nif.test);
4301 cmdputs("; then ");
4302 n = n->nif.ifpart;
4303 if (n->nif.elsepart) {
4304 cmdtxt(n);
4305 cmdputs("; else ");
4306 n = n->nif.elsepart;
4307 }
4308 p = "; fi";
4309 goto dotail;
4310 case NSUBSHELL:
4311 cmdputs("(");
4312 n = n->nredir.n;
4313 p = ")";
4314 goto dotail;
4315 case NWHILE:
4316 p = "while ";
4317 goto until;
4318 case NUNTIL:
4319 p = "until ";
4320 until:
4321 cmdputs(p);
4322 cmdtxt(n->nbinary.ch1);
4323 n = n->nbinary.ch2;
4324 p = "; done";
4325 dodo:
4326 cmdputs("; do ");
4327 dotail:
4328 cmdtxt(n);
4329 goto dotail2;
4330 case NFOR:
4331 cmdputs("for ");
4332 cmdputs(n->nfor.var);
4333 cmdputs(" in ");
4334 cmdlist(n->nfor.args, 1);
4335 n = n->nfor.body;
4336 p = "; done";
4337 goto dodo;
4338 case NDEFUN:
4339 cmdputs(n->narg.text);
4340 p = "() { ... }";
4341 goto dotail2;
4342 case NCMD:
4343 cmdlist(n->ncmd.args, 1);
4344 cmdlist(n->ncmd.redirect, 0);
4345 break;
4346 case NARG:
4347 p = n->narg.text;
4348 dotail2:
4349 cmdputs(p);
4350 break;
4351 case NHERE:
4352 case NXHERE:
4353 p = "<<...";
4354 goto dotail2;
4355 case NCASE:
4356 cmdputs("case ");
4357 cmdputs(n->ncase.expr->narg.text);
4358 cmdputs(" in ");
4359 for (np = n->ncase.cases; np; np = np->nclist.next) {
4360 cmdtxt(np->nclist.pattern);
4361 cmdputs(") ");
4362 cmdtxt(np->nclist.body);
4363 cmdputs(";; ");
4364 }
4365 p = "esac";
4366 goto dotail2;
4367 case NTO:
4368 p = ">";
4369 goto redir;
4370 case NCLOBBER:
4371 p = ">|";
4372 goto redir;
4373 case NAPPEND:
4374 p = ">>";
4375 goto redir;
4376 case NTOFD:
4377 p = ">&";
4378 goto redir;
4379 case NFROM:
4380 p = "<";
4381 goto redir;
4382 case NFROMFD:
4383 p = "<&";
4384 goto redir;
4385 case NFROMTO:
4386 p = "<>";
4387 redir:
4388 s[0] = n->nfile.fd + '0';
4389 s[1] = '\0';
4390 cmdputs(s);
4391 cmdputs(p);
4392 if (n->type == NTOFD || n->type == NFROMFD) {
4393 s[0] = n->ndup.dupfd + '0';
4394 p = s;
4395 goto dotail2;
4396 }
4397 n = n->nfile.fname;
4398 goto donode;
4399 }
4400}
4401
4402static char *
4403commandtext(union node *n)
4404{
4405 char *name;
4406
4407 STARTSTACKSTR(cmdnextc);
4408 cmdtxt(n);
4409 name = stackblock();
4410 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4411 name, cmdnextc, cmdnextc));
4412 return ckstrdup(name);
4413}
4414#endif /* JOBS */
4415
4416/*
4417 * Fork off a subshell. If we are doing job control, give the subshell its
4418 * own process group. Jp is a job structure that the job is to be added to.
4419 * N is the command that will be evaluated by the child. Both jp and n may
4420 * be NULL. The mode parameter can be one of the following:
4421 * FORK_FG - Fork off a foreground process.
4422 * FORK_BG - Fork off a background process.
4423 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4424 * process group even if job control is on.
4425 *
4426 * When job control is turned off, background processes have their standard
4427 * input redirected to /dev/null (except for the second and later processes
4428 * in a pipeline).
4429 *
4430 * Called with interrupts off.
4431 */
4432/*
4433 * Clear traps on a fork.
4434 */
4435static void
4436clear_traps(void)
4437{
4438 char **tp;
4439
4440 for (tp = trap; tp < &trap[NSIG]; tp++) {
4441 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4442 INT_OFF;
4443 free(*tp);
4444 *tp = NULL;
4445 if (tp != &trap[0])
4446 setsignal(tp - trap);
4447 INT_ON;
4448 }
4449 }
4450}
4451/* lives far away from here, needed for forkchild */
4452static void closescript(void);
4453static void
4454forkchild(struct job *jp, union node *n, int mode)
4455{
4456 int oldlvl;
4457
4458 TRACE(("Child shell %d\n", getpid()));
4459 oldlvl = shlvl;
4460 shlvl++;
4461
4462 closescript();
4463 clear_traps();
4464#if JOBS
4465 /* do job control only in root shell */
4466 jobctl = 0;
4467 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4468 pid_t pgrp;
4469
4470 if (jp->nprocs == 0)
4471 pgrp = getpid();
4472 else
4473 pgrp = jp->ps[0].pid;
4474 /* This can fail because we are doing it in the parent also */
4475 (void)setpgid(0, pgrp);
4476 if (mode == FORK_FG)
4477 xtcsetpgrp(ttyfd, pgrp);
4478 setsignal(SIGTSTP);
4479 setsignal(SIGTTOU);
4480 } else
4481#endif
4482 if (mode == FORK_BG) {
4483 ignoresig(SIGINT);
4484 ignoresig(SIGQUIT);
4485 if (jp->nprocs == 0) {
4486 close(0);
4487 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004488 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489 }
4490 }
4491 if (!oldlvl && iflag) {
4492 setsignal(SIGINT);
4493 setsignal(SIGQUIT);
4494 setsignal(SIGTERM);
4495 }
4496 for (jp = curjob; jp; jp = jp->prev_job)
4497 freejob(jp);
4498 jobless = 0;
4499}
4500
4501static void
4502forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4503{
4504 TRACE(("In parent shell: child = %d\n", pid));
4505 if (!jp) {
4506 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4507 jobless++;
4508 return;
4509 }
4510#if JOBS
4511 if (mode != FORK_NOJOB && jp->jobctl) {
4512 int pgrp;
4513
4514 if (jp->nprocs == 0)
4515 pgrp = pid;
4516 else
4517 pgrp = jp->ps[0].pid;
4518 /* This can fail because we are doing it in the child also */
4519 setpgid(pid, pgrp);
4520 }
4521#endif
4522 if (mode == FORK_BG) {
4523 backgndpid = pid; /* set $! */
4524 set_curjob(jp, CUR_RUNNING);
4525 }
4526 if (jp) {
4527 struct procstat *ps = &jp->ps[jp->nprocs++];
4528 ps->pid = pid;
4529 ps->status = -1;
4530 ps->cmd = nullstr;
4531#if JOBS
4532 if (jobctl && n)
4533 ps->cmd = commandtext(n);
4534#endif
4535 }
4536}
4537
4538static int
4539forkshell(struct job *jp, union node *n, int mode)
4540{
4541 int pid;
4542
4543 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4544 pid = fork();
4545 if (pid < 0) {
4546 TRACE(("Fork failed, errno=%d", errno));
4547 if (jp)
4548 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004549 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004550 }
4551 if (pid == 0)
4552 forkchild(jp, n, mode);
4553 else
4554 forkparent(jp, n, mode, pid);
4555 return pid;
4556}
4557
4558/*
4559 * Wait for job to finish.
4560 *
4561 * Under job control we have the problem that while a child process is
4562 * running interrupts generated by the user are sent to the child but not
4563 * to the shell. This means that an infinite loop started by an inter-
4564 * active user may be hard to kill. With job control turned off, an
4565 * interactive user may place an interactive program inside a loop. If
4566 * the interactive program catches interrupts, the user doesn't want
4567 * these interrupts to also abort the loop. The approach we take here
4568 * is to have the shell ignore interrupt signals while waiting for a
4569 * foreground process to terminate, and then send itself an interrupt
4570 * signal if the child process was terminated by an interrupt signal.
4571 * Unfortunately, some programs want to do a bit of cleanup and then
4572 * exit on interrupt; unless these processes terminate themselves by
4573 * sending a signal to themselves (instead of calling exit) they will
4574 * confuse this approach.
4575 *
4576 * Called with interrupts off.
4577 */
4578static int
4579waitforjob(struct job *jp)
4580{
4581 int st;
4582
4583 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4584 while (jp->state == JOBRUNNING) {
4585 dowait(DOWAIT_BLOCK, jp);
4586 }
4587 st = getstatus(jp);
4588#if JOBS
4589 if (jp->jobctl) {
4590 xtcsetpgrp(ttyfd, rootpid);
4591 /*
4592 * This is truly gross.
4593 * If we're doing job control, then we did a TIOCSPGRP which
4594 * caused us (the shell) to no longer be in the controlling
4595 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4596 * intuit from the subprocess exit status whether a SIGINT
4597 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4598 */
4599 if (jp->sigint)
4600 raise(SIGINT);
4601 }
4602 if (jp->state == JOBDONE)
4603#endif
4604 freejob(jp);
4605 return st;
4606}
4607
4608/*
4609 * return 1 if there are stopped jobs, otherwise 0
4610 */
4611static int
4612stoppedjobs(void)
4613{
4614 struct job *jp;
4615 int retval;
4616
4617 retval = 0;
4618 if (job_warning)
4619 goto out;
4620 jp = curjob;
4621 if (jp && jp->state == JOBSTOPPED) {
4622 out2str("You have stopped jobs.\n");
4623 job_warning = 2;
4624 retval++;
4625 }
4626 out:
4627 return retval;
4628}
4629
4630
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004631/* ============ redir.c
4632 *
4633 * Code for dealing with input/output redirection.
4634 */
4635
4636#define EMPTY -2 /* marks an unused slot in redirtab */
4637#ifndef PIPE_BUF
4638# define PIPESIZE 4096 /* amount of buffering in a pipe */
4639#else
4640# define PIPESIZE PIPE_BUF
4641#endif
4642
4643/*
4644 * Open a file in noclobber mode.
4645 * The code was copied from bash.
4646 */
4647static int
4648noclobberopen(const char *fname)
4649{
4650 int r, fd;
4651 struct stat finfo, finfo2;
4652
4653 /*
4654 * If the file exists and is a regular file, return an error
4655 * immediately.
4656 */
4657 r = stat(fname, &finfo);
4658 if (r == 0 && S_ISREG(finfo.st_mode)) {
4659 errno = EEXIST;
4660 return -1;
4661 }
4662
4663 /*
4664 * If the file was not present (r != 0), make sure we open it
4665 * exclusively so that if it is created before we open it, our open
4666 * will fail. Make sure that we do not truncate an existing file.
4667 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4668 * file was not a regular file, we leave O_EXCL off.
4669 */
4670 if (r != 0)
4671 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4672 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4673
4674 /* If the open failed, return the file descriptor right away. */
4675 if (fd < 0)
4676 return fd;
4677
4678 /*
4679 * OK, the open succeeded, but the file may have been changed from a
4680 * non-regular file to a regular file between the stat and the open.
4681 * We are assuming that the O_EXCL open handles the case where FILENAME
4682 * did not exist and is symlinked to an existing file between the stat
4683 * and open.
4684 */
4685
4686 /*
4687 * If we can open it and fstat the file descriptor, and neither check
4688 * revealed that it was a regular file, and the file has not been
4689 * replaced, return the file descriptor.
4690 */
4691 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4692 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4693 return fd;
4694
4695 /* The file has been replaced. badness. */
4696 close(fd);
4697 errno = EEXIST;
4698 return -1;
4699}
4700
4701/*
4702 * Handle here documents. Normally we fork off a process to write the
4703 * data to a pipe. If the document is short, we can stuff the data in
4704 * the pipe without forking.
4705 */
4706/* openhere needs this forward reference */
4707static void expandhere(union node *arg, int fd);
4708static int
4709openhere(union node *redir)
4710{
4711 int pip[2];
4712 size_t len = 0;
4713
4714 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004715 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004716 if (redir->type == NHERE) {
4717 len = strlen(redir->nhere.doc->narg.text);
4718 if (len <= PIPESIZE) {
4719 full_write(pip[1], redir->nhere.doc->narg.text, len);
4720 goto out;
4721 }
4722 }
4723 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4724 close(pip[0]);
4725 signal(SIGINT, SIG_IGN);
4726 signal(SIGQUIT, SIG_IGN);
4727 signal(SIGHUP, SIG_IGN);
4728#ifdef SIGTSTP
4729 signal(SIGTSTP, SIG_IGN);
4730#endif
4731 signal(SIGPIPE, SIG_DFL);
4732 if (redir->type == NHERE)
4733 full_write(pip[1], redir->nhere.doc->narg.text, len);
4734 else
4735 expandhere(redir->nhere.doc, pip[1]);
4736 _exit(0);
4737 }
4738 out:
4739 close(pip[1]);
4740 return pip[0];
4741}
4742
4743static int
4744openredirect(union node *redir)
4745{
4746 char *fname;
4747 int f;
4748
4749 switch (redir->nfile.type) {
4750 case NFROM:
4751 fname = redir->nfile.expfname;
4752 f = open(fname, O_RDONLY);
4753 if (f < 0)
4754 goto eopen;
4755 break;
4756 case NFROMTO:
4757 fname = redir->nfile.expfname;
4758 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4759 if (f < 0)
4760 goto ecreate;
4761 break;
4762 case NTO:
4763 /* Take care of noclobber mode. */
4764 if (Cflag) {
4765 fname = redir->nfile.expfname;
4766 f = noclobberopen(fname);
4767 if (f < 0)
4768 goto ecreate;
4769 break;
4770 }
4771 /* FALLTHROUGH */
4772 case NCLOBBER:
4773 fname = redir->nfile.expfname;
4774 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4775 if (f < 0)
4776 goto ecreate;
4777 break;
4778 case NAPPEND:
4779 fname = redir->nfile.expfname;
4780 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4781 if (f < 0)
4782 goto ecreate;
4783 break;
4784 default:
4785#if DEBUG
4786 abort();
4787#endif
4788 /* Fall through to eliminate warning. */
4789 case NTOFD:
4790 case NFROMFD:
4791 f = -1;
4792 break;
4793 case NHERE:
4794 case NXHERE:
4795 f = openhere(redir);
4796 break;
4797 }
4798
4799 return f;
4800 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004801 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004802 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004803 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004804}
4805
4806/*
4807 * Copy a file descriptor to be >= to. Returns -1
4808 * if the source file descriptor is closed, EMPTY if there are no unused
4809 * file descriptors left.
4810 */
4811static int
4812copyfd(int from, int to)
4813{
4814 int newfd;
4815
4816 newfd = fcntl(from, F_DUPFD, to);
4817 if (newfd < 0) {
4818 if (errno == EMFILE)
4819 return EMPTY;
4820 ash_msg_and_raise_error("%d: %m", from);
4821 }
4822 return newfd;
4823}
4824
4825static void
4826dupredirect(union node *redir, int f)
4827{
4828 int fd = redir->nfile.fd;
4829
4830 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4831 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4832 copyfd(redir->ndup.dupfd, fd);
4833 }
4834 return;
4835 }
4836
4837 if (f != fd) {
4838 copyfd(f, fd);
4839 close(f);
4840 }
4841}
4842
4843/*
4844 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4845 * old file descriptors are stashed away so that the redirection can be
4846 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4847 * standard output, and the standard error if it becomes a duplicate of
4848 * stdout, is saved in memory.
4849 */
4850/* flags passed to redirect */
4851#define REDIR_PUSH 01 /* save previous values of file descriptors */
4852#define REDIR_SAVEFD2 03 /* set preverrout */
4853static void
4854redirect(union node *redir, int flags)
4855{
4856 union node *n;
4857 struct redirtab *sv;
4858 int i;
4859 int fd;
4860 int newfd;
4861 int *p;
4862 nullredirs++;
4863 if (!redir) {
4864 return;
4865 }
4866 sv = NULL;
4867 INT_OFF;
4868 if (flags & REDIR_PUSH) {
4869 struct redirtab *q;
4870 q = ckmalloc(sizeof(struct redirtab));
4871 q->next = redirlist;
4872 redirlist = q;
4873 q->nullredirs = nullredirs - 1;
4874 for (i = 0; i < 10; i++)
4875 q->renamed[i] = EMPTY;
4876 nullredirs = 0;
4877 sv = q;
4878 }
4879 n = redir;
4880 do {
4881 fd = n->nfile.fd;
4882 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4883 && n->ndup.dupfd == fd)
4884 continue; /* redirect from/to same file descriptor */
4885
4886 newfd = openredirect(n);
4887 if (fd == newfd)
4888 continue;
4889 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4890 i = fcntl(fd, F_DUPFD, 10);
4891
4892 if (i == -1) {
4893 i = errno;
4894 if (i != EBADF) {
4895 close(newfd);
4896 errno = i;
4897 ash_msg_and_raise_error("%d: %m", fd);
4898 /* NOTREACHED */
4899 }
4900 } else {
4901 *p = i;
4902 close(fd);
4903 }
4904 } else {
4905 close(fd);
4906 }
4907 dupredirect(n, newfd);
4908 } while ((n = n->nfile.next));
4909 INT_ON;
4910 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4911 preverrout_fd = sv->renamed[2];
4912}
4913
4914/*
4915 * Undo the effects of the last redirection.
4916 */
4917static void
4918popredir(int drop)
4919{
4920 struct redirtab *rp;
4921 int i;
4922
4923 if (--nullredirs >= 0)
4924 return;
4925 INT_OFF;
4926 rp = redirlist;
4927 for (i = 0; i < 10; i++) {
4928 if (rp->renamed[i] != EMPTY) {
4929 if (!drop) {
4930 close(i);
4931 copyfd(rp->renamed[i], i);
4932 }
4933 close(rp->renamed[i]);
4934 }
4935 }
4936 redirlist = rp->next;
4937 nullredirs = rp->nullredirs;
4938 free(rp);
4939 INT_ON;
4940}
4941
4942/*
4943 * Undo all redirections. Called on error or interrupt.
4944 */
4945
4946/*
4947 * Discard all saved file descriptors.
4948 */
4949static void
4950clearredir(int drop)
4951{
4952 for (;;) {
4953 nullredirs = 0;
4954 if (!redirlist)
4955 break;
4956 popredir(drop);
4957 }
4958}
4959
4960static int
4961redirectsafe(union node *redir, int flags)
4962{
4963 int err;
4964 volatile int saveint;
4965 struct jmploc *volatile savehandler = exception_handler;
4966 struct jmploc jmploc;
4967
4968 SAVE_INT(saveint);
4969 err = setjmp(jmploc.loc) * 2;
4970 if (!err) {
4971 exception_handler = &jmploc;
4972 redirect(redir, flags);
4973 }
4974 exception_handler = savehandler;
4975 if (err && exception != EXERROR)
4976 longjmp(exception_handler->loc, 1);
4977 RESTORE_INT(saveint);
4978 return err;
4979}
4980
4981
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004982/* ============ Routines to expand arguments to commands
4983 *
4984 * We have to deal with backquotes, shell variables, and file metacharacters.
4985 */
4986
4987/*
4988 * expandarg flags
4989 */
4990#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4991#define EXP_TILDE 0x2 /* do normal tilde expansion */
4992#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4993#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4994#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4995#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4996#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4997#define EXP_WORD 0x80 /* expand word in parameter expansion */
4998#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4999/*
5000 * _rmescape() flags
5001 */
5002#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5003#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5004#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5005#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5006#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5007
5008/*
5009 * Structure specifying which parts of the string should be searched
5010 * for IFS characters.
5011 */
5012struct ifsregion {
5013 struct ifsregion *next; /* next region in list */
5014 int begoff; /* offset of start of region */
5015 int endoff; /* offset of end of region */
5016 int nulonly; /* search for nul bytes only */
5017};
5018
5019struct arglist {
5020 struct strlist *list;
5021 struct strlist **lastp;
5022};
5023
5024/* output of current string */
5025static char *expdest;
5026/* list of back quote expressions */
5027static struct nodelist *argbackq;
5028/* first struct in list of ifs regions */
5029static struct ifsregion ifsfirst;
5030/* last struct in list */
5031static struct ifsregion *ifslastp;
5032/* holds expanded arg list */
5033static struct arglist exparg;
5034
5035/*
5036 * Our own itoa().
5037 */
5038static int
5039cvtnum(arith_t num)
5040{
5041 int len;
5042
5043 expdest = makestrspace(32, expdest);
5044#if ENABLE_ASH_MATH_SUPPORT_64
5045 len = fmtstr(expdest, 32, "%lld", (long long) num);
5046#else
5047 len = fmtstr(expdest, 32, "%ld", num);
5048#endif
5049 STADJUST(len, expdest);
5050 return len;
5051}
5052
5053static size_t
5054esclen(const char *start, const char *p)
5055{
5056 size_t esc = 0;
5057
5058 while (p > start && *--p == CTLESC) {
5059 esc++;
5060 }
5061 return esc;
5062}
5063
5064/*
5065 * Remove any CTLESC characters from a string.
5066 */
5067static char *
5068_rmescapes(char *str, int flag)
5069{
5070 char *p, *q, *r;
5071 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5072 unsigned inquotes;
5073 int notescaped;
5074 int globbing;
5075
5076 p = strpbrk(str, qchars);
5077 if (!p) {
5078 return str;
5079 }
5080 q = p;
5081 r = str;
5082 if (flag & RMESCAPE_ALLOC) {
5083 size_t len = p - str;
5084 size_t fulllen = len + strlen(p) + 1;
5085
5086 if (flag & RMESCAPE_GROW) {
5087 r = makestrspace(fulllen, expdest);
5088 } else if (flag & RMESCAPE_HEAP) {
5089 r = ckmalloc(fulllen);
5090 } else {
5091 r = stalloc(fulllen);
5092 }
5093 q = r;
5094 if (len > 0) {
5095 q = memcpy(q, str, len) + len;
5096 }
5097 }
5098 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5099 globbing = flag & RMESCAPE_GLOB;
5100 notescaped = globbing;
5101 while (*p) {
5102 if (*p == CTLQUOTEMARK) {
5103 inquotes = ~inquotes;
5104 p++;
5105 notescaped = globbing;
5106 continue;
5107 }
5108 if (*p == '\\') {
5109 /* naked back slash */
5110 notescaped = 0;
5111 goto copy;
5112 }
5113 if (*p == CTLESC) {
5114 p++;
5115 if (notescaped && inquotes && *p != '/') {
5116 *q++ = '\\';
5117 }
5118 }
5119 notescaped = globbing;
5120 copy:
5121 *q++ = *p++;
5122 }
5123 *q = '\0';
5124 if (flag & RMESCAPE_GROW) {
5125 expdest = r;
5126 STADJUST(q - r + 1, expdest);
5127 }
5128 return r;
5129}
5130#define rmescapes(p) _rmescapes((p), 0)
5131
5132#define pmatch(a, b) !fnmatch((a), (b), 0)
5133
5134/*
5135 * Prepare a pattern for a expmeta (internal glob(3)) call.
5136 *
5137 * Returns an stalloced string.
5138 */
5139static char *
5140preglob(const char *pattern, int quoted, int flag)
5141{
5142 flag |= RMESCAPE_GLOB;
5143 if (quoted) {
5144 flag |= RMESCAPE_QUOTED;
5145 }
5146 return _rmescapes((char *)pattern, flag);
5147}
5148
5149/*
5150 * Put a string on the stack.
5151 */
5152static void
5153memtodest(const char *p, size_t len, int syntax, int quotes)
5154{
5155 char *q = expdest;
5156
5157 q = makestrspace(len * 2, q);
5158
5159 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005160 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005161 if (!c)
5162 continue;
5163 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5164 USTPUTC(CTLESC, q);
5165 USTPUTC(c, q);
5166 }
5167
5168 expdest = q;
5169}
5170
5171static void
5172strtodest(const char *p, int syntax, int quotes)
5173{
5174 memtodest(p, strlen(p), syntax, quotes);
5175}
5176
5177/*
5178 * Record the fact that we have to scan this region of the
5179 * string for IFS characters.
5180 */
5181static void
5182recordregion(int start, int end, int nulonly)
5183{
5184 struct ifsregion *ifsp;
5185
5186 if (ifslastp == NULL) {
5187 ifsp = &ifsfirst;
5188 } else {
5189 INT_OFF;
5190 ifsp = ckmalloc(sizeof(*ifsp));
5191 ifsp->next = NULL;
5192 ifslastp->next = ifsp;
5193 INT_ON;
5194 }
5195 ifslastp = ifsp;
5196 ifslastp->begoff = start;
5197 ifslastp->endoff = end;
5198 ifslastp->nulonly = nulonly;
5199}
5200
5201static void
5202removerecordregions(int endoff)
5203{
5204 if (ifslastp == NULL)
5205 return;
5206
5207 if (ifsfirst.endoff > endoff) {
5208 while (ifsfirst.next != NULL) {
5209 struct ifsregion *ifsp;
5210 INT_OFF;
5211 ifsp = ifsfirst.next->next;
5212 free(ifsfirst.next);
5213 ifsfirst.next = ifsp;
5214 INT_ON;
5215 }
5216 if (ifsfirst.begoff > endoff)
5217 ifslastp = NULL;
5218 else {
5219 ifslastp = &ifsfirst;
5220 ifsfirst.endoff = endoff;
5221 }
5222 return;
5223 }
5224
5225 ifslastp = &ifsfirst;
5226 while (ifslastp->next && ifslastp->next->begoff < endoff)
5227 ifslastp=ifslastp->next;
5228 while (ifslastp->next != NULL) {
5229 struct ifsregion *ifsp;
5230 INT_OFF;
5231 ifsp = ifslastp->next->next;
5232 free(ifslastp->next);
5233 ifslastp->next = ifsp;
5234 INT_ON;
5235 }
5236 if (ifslastp->endoff > endoff)
5237 ifslastp->endoff = endoff;
5238}
5239
5240static char *
5241exptilde(char *startp, char *p, int flag)
5242{
5243 char c;
5244 char *name;
5245 struct passwd *pw;
5246 const char *home;
5247 int quotes = flag & (EXP_FULL | EXP_CASE);
5248 int startloc;
5249
5250 name = p + 1;
5251
5252 while ((c = *++p) != '\0') {
5253 switch (c) {
5254 case CTLESC:
5255 return startp;
5256 case CTLQUOTEMARK:
5257 return startp;
5258 case ':':
5259 if (flag & EXP_VARTILDE)
5260 goto done;
5261 break;
5262 case '/':
5263 case CTLENDVAR:
5264 goto done;
5265 }
5266 }
5267 done:
5268 *p = '\0';
5269 if (*name == '\0') {
5270 home = lookupvar(homestr);
5271 } else {
5272 pw = getpwnam(name);
5273 if (pw == NULL)
5274 goto lose;
5275 home = pw->pw_dir;
5276 }
5277 if (!home || !*home)
5278 goto lose;
5279 *p = c;
5280 startloc = expdest - (char *)stackblock();
5281 strtodest(home, SQSYNTAX, quotes);
5282 recordregion(startloc, expdest - (char *)stackblock(), 0);
5283 return p;
5284 lose:
5285 *p = c;
5286 return startp;
5287}
5288
5289/*
5290 * Execute a command inside back quotes. If it's a builtin command, we
5291 * want to save its output in a block obtained from malloc. Otherwise
5292 * we fork off a subprocess and get the output of the command via a pipe.
5293 * Should be called with interrupts off.
5294 */
5295struct backcmd { /* result of evalbackcmd */
5296 int fd; /* file descriptor to read from */
5297 char *buf; /* buffer */
5298 int nleft; /* number of chars in buffer */
5299 struct job *jp; /* job structure for command */
5300};
5301
5302/* These forward decls are needed to use "eval" code for backticks handling: */
5303static int back_exitstatus; /* exit status of backquoted command */
5304#define EV_EXIT 01 /* exit after evaluating tree */
5305static void evaltree(union node *, int);
5306
5307static void
5308evalbackcmd(union node *n, struct backcmd *result)
5309{
5310 int saveherefd;
5311
5312 result->fd = -1;
5313 result->buf = NULL;
5314 result->nleft = 0;
5315 result->jp = NULL;
5316 if (n == NULL) {
5317 goto out;
5318 }
5319
5320 saveherefd = herefd;
5321 herefd = -1;
5322
5323 {
5324 int pip[2];
5325 struct job *jp;
5326
5327 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005328 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005329 jp = makejob(n, 1);
5330 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5331 FORCE_INT_ON;
5332 close(pip[0]);
5333 if (pip[1] != 1) {
5334 close(1);
5335 copyfd(pip[1], 1);
5336 close(pip[1]);
5337 }
5338 eflag = 0;
5339 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5340 /* NOTREACHED */
5341 }
5342 close(pip[1]);
5343 result->fd = pip[0];
5344 result->jp = jp;
5345 }
5346 herefd = saveherefd;
5347 out:
5348 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5349 result->fd, result->buf, result->nleft, result->jp));
5350}
5351
5352/*
5353 * Expand stuff in backwards quotes.
5354 */
5355static void
5356expbackq(union node *cmd, int quoted, int quotes)
5357{
5358 struct backcmd in;
5359 int i;
5360 char buf[128];
5361 char *p;
5362 char *dest;
5363 int startloc;
5364 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5365 struct stackmark smark;
5366
5367 INT_OFF;
5368 setstackmark(&smark);
5369 dest = expdest;
5370 startloc = dest - (char *)stackblock();
5371 grabstackstr(dest);
5372 evalbackcmd(cmd, &in);
5373 popstackmark(&smark);
5374
5375 p = in.buf;
5376 i = in.nleft;
5377 if (i == 0)
5378 goto read;
5379 for (;;) {
5380 memtodest(p, i, syntax, quotes);
5381 read:
5382 if (in.fd < 0)
5383 break;
5384 i = safe_read(in.fd, buf, sizeof(buf));
5385 TRACE(("expbackq: read returns %d\n", i));
5386 if (i <= 0)
5387 break;
5388 p = buf;
5389 }
5390
5391 if (in.buf)
5392 free(in.buf);
5393 if (in.fd >= 0) {
5394 close(in.fd);
5395 back_exitstatus = waitforjob(in.jp);
5396 }
5397 INT_ON;
5398
5399 /* Eat all trailing newlines */
5400 dest = expdest;
5401 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5402 STUNPUTC(dest);
5403 expdest = dest;
5404
5405 if (quoted == 0)
5406 recordregion(startloc, dest - (char *)stackblock(), 0);
5407 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5408 (dest - (char *)stackblock()) - startloc,
5409 (dest - (char *)stackblock()) - startloc,
5410 stackblock() + startloc));
5411}
5412
5413#if ENABLE_ASH_MATH_SUPPORT
5414/*
5415 * Expand arithmetic expression. Backup to start of expression,
5416 * evaluate, place result in (backed up) result, adjust string position.
5417 */
5418static void
5419expari(int quotes)
5420{
5421 char *p, *start;
5422 int begoff;
5423 int flag;
5424 int len;
5425
5426 /* ifsfree(); */
5427
5428 /*
5429 * This routine is slightly over-complicated for
5430 * efficiency. Next we scan backwards looking for the
5431 * start of arithmetic.
5432 */
5433 start = stackblock();
5434 p = expdest - 1;
5435 *p = '\0';
5436 p--;
5437 do {
5438 int esc;
5439
5440 while (*p != CTLARI) {
5441 p--;
5442#if DEBUG
5443 if (p < start) {
5444 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5445 }
5446#endif
5447 }
5448
5449 esc = esclen(start, p);
5450 if (!(esc % 2)) {
5451 break;
5452 }
5453
5454 p -= esc + 1;
5455 } while (1);
5456
5457 begoff = p - start;
5458
5459 removerecordregions(begoff);
5460
5461 flag = p[1];
5462
5463 expdest = p;
5464
5465 if (quotes)
5466 rmescapes(p + 2);
5467
5468 len = cvtnum(dash_arith(p + 2));
5469
5470 if (flag != '"')
5471 recordregion(begoff, begoff + len, 0);
5472}
5473#endif
5474
5475/* argstr needs it */
5476static char *evalvar(char *p, int flag);
5477
5478/*
5479 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5480 * characters to allow for further processing. Otherwise treat
5481 * $@ like $* since no splitting will be performed.
5482 */
5483static void
5484argstr(char *p, int flag)
5485{
5486 static const char spclchars[] = {
5487 '=',
5488 ':',
5489 CTLQUOTEMARK,
5490 CTLENDVAR,
5491 CTLESC,
5492 CTLVAR,
5493 CTLBACKQ,
5494 CTLBACKQ | CTLQUOTE,
5495#if ENABLE_ASH_MATH_SUPPORT
5496 CTLENDARI,
5497#endif
5498 0
5499 };
5500 const char *reject = spclchars;
5501 int c;
5502 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5503 int breakall = flag & EXP_WORD;
5504 int inquotes;
5505 size_t length;
5506 int startloc;
5507
5508 if (!(flag & EXP_VARTILDE)) {
5509 reject += 2;
5510 } else if (flag & EXP_VARTILDE2) {
5511 reject++;
5512 }
5513 inquotes = 0;
5514 length = 0;
5515 if (flag & EXP_TILDE) {
5516 char *q;
5517
5518 flag &= ~EXP_TILDE;
5519 tilde:
5520 q = p;
5521 if (*q == CTLESC && (flag & EXP_QWORD))
5522 q++;
5523 if (*q == '~')
5524 p = exptilde(p, q, flag);
5525 }
5526 start:
5527 startloc = expdest - (char *)stackblock();
5528 for (;;) {
5529 length += strcspn(p + length, reject);
5530 c = p[length];
5531 if (c && (!(c & 0x80)
5532#if ENABLE_ASH_MATH_SUPPORT
5533 || c == CTLENDARI
5534#endif
5535 )) {
5536 /* c == '=' || c == ':' || c == CTLENDARI */
5537 length++;
5538 }
5539 if (length > 0) {
5540 int newloc;
5541 expdest = stack_nputstr(p, length, expdest);
5542 newloc = expdest - (char *)stackblock();
5543 if (breakall && !inquotes && newloc > startloc) {
5544 recordregion(startloc, newloc, 0);
5545 }
5546 startloc = newloc;
5547 }
5548 p += length + 1;
5549 length = 0;
5550
5551 switch (c) {
5552 case '\0':
5553 goto breakloop;
5554 case '=':
5555 if (flag & EXP_VARTILDE2) {
5556 p--;
5557 continue;
5558 }
5559 flag |= EXP_VARTILDE2;
5560 reject++;
5561 /* fall through */
5562 case ':':
5563 /*
5564 * sort of a hack - expand tildes in variable
5565 * assignments (after the first '=' and after ':'s).
5566 */
5567 if (*--p == '~') {
5568 goto tilde;
5569 }
5570 continue;
5571 }
5572
5573 switch (c) {
5574 case CTLENDVAR: /* ??? */
5575 goto breakloop;
5576 case CTLQUOTEMARK:
5577 /* "$@" syntax adherence hack */
5578 if (
5579 !inquotes &&
5580 !memcmp(p, dolatstr, 4) &&
5581 (p[4] == CTLQUOTEMARK || (
5582 p[4] == CTLENDVAR &&
5583 p[5] == CTLQUOTEMARK
5584 ))
5585 ) {
5586 p = evalvar(p + 1, flag) + 1;
5587 goto start;
5588 }
5589 inquotes = !inquotes;
5590 addquote:
5591 if (quotes) {
5592 p--;
5593 length++;
5594 startloc++;
5595 }
5596 break;
5597 case CTLESC:
5598 startloc++;
5599 length++;
5600 goto addquote;
5601 case CTLVAR:
5602 p = evalvar(p, flag);
5603 goto start;
5604 case CTLBACKQ:
5605 c = 0;
5606 case CTLBACKQ|CTLQUOTE:
5607 expbackq(argbackq->n, c, quotes);
5608 argbackq = argbackq->next;
5609 goto start;
5610#if ENABLE_ASH_MATH_SUPPORT
5611 case CTLENDARI:
5612 p--;
5613 expari(quotes);
5614 goto start;
5615#endif
5616 }
5617 }
5618 breakloop:
5619 ;
5620}
5621
5622static char *
5623scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5624 int zero)
5625{
5626 char *loc;
5627 char *loc2;
5628 char c;
5629
5630 loc = startp;
5631 loc2 = rmesc;
5632 do {
5633 int match;
5634 const char *s = loc2;
5635 c = *loc2;
5636 if (zero) {
5637 *loc2 = '\0';
5638 s = rmesc;
5639 }
5640 match = pmatch(str, s);
5641 *loc2 = c;
5642 if (match)
5643 return loc;
5644 if (quotes && *loc == CTLESC)
5645 loc++;
5646 loc++;
5647 loc2++;
5648 } while (c);
5649 return 0;
5650}
5651
5652static char *
5653scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5654 int zero)
5655{
5656 int esc = 0;
5657 char *loc;
5658 char *loc2;
5659
5660 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5661 int match;
5662 char c = *loc2;
5663 const char *s = loc2;
5664 if (zero) {
5665 *loc2 = '\0';
5666 s = rmesc;
5667 }
5668 match = pmatch(str, s);
5669 *loc2 = c;
5670 if (match)
5671 return loc;
5672 loc--;
5673 if (quotes) {
5674 if (--esc < 0) {
5675 esc = esclen(startp, loc);
5676 }
5677 if (esc % 2) {
5678 esc--;
5679 loc--;
5680 }
5681 }
5682 }
5683 return 0;
5684}
5685
5686static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5687static void
5688varunset(const char *end, const char *var, const char *umsg, int varflags)
5689{
5690 const char *msg;
5691 const char *tail;
5692
5693 tail = nullstr;
5694 msg = "parameter not set";
5695 if (umsg) {
5696 if (*end == CTLENDVAR) {
5697 if (varflags & VSNUL)
5698 tail = " or null";
5699 } else
5700 msg = umsg;
5701 }
5702 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5703}
5704
5705static const char *
5706subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5707{
5708 char *startp;
5709 char *loc;
5710 int saveherefd = herefd;
5711 struct nodelist *saveargbackq = argbackq;
5712 int amount;
5713 char *rmesc, *rmescend;
5714 int zero;
5715 char *(*scan)(char *, char *, char *, char *, int , int);
5716
5717 herefd = -1;
5718 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5719 STPUTC('\0', expdest);
5720 herefd = saveherefd;
5721 argbackq = saveargbackq;
5722 startp = stackblock() + startloc;
5723
5724 switch (subtype) {
5725 case VSASSIGN:
5726 setvar(str, startp, 0);
5727 amount = startp - expdest;
5728 STADJUST(amount, expdest);
5729 return startp;
5730
5731 case VSQUESTION:
5732 varunset(p, str, startp, varflags);
5733 /* NOTREACHED */
5734 }
5735
5736 subtype -= VSTRIMRIGHT;
5737#if DEBUG
5738 if (subtype < 0 || subtype > 3)
5739 abort();
5740#endif
5741
5742 rmesc = startp;
5743 rmescend = stackblock() + strloc;
5744 if (quotes) {
5745 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5746 if (rmesc != startp) {
5747 rmescend = expdest;
5748 startp = stackblock() + startloc;
5749 }
5750 }
5751 rmescend--;
5752 str = stackblock() + strloc;
5753 preglob(str, varflags & VSQUOTE, 0);
5754
5755 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5756 zero = subtype >> 1;
5757 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5758 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5759
5760 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5761 if (loc) {
5762 if (zero) {
5763 memmove(startp, loc, str - loc);
5764 loc = startp + (str - loc) - 1;
5765 }
5766 *loc = '\0';
5767 amount = loc - expdest;
5768 STADJUST(amount, expdest);
5769 }
5770 return loc;
5771}
5772
5773/*
5774 * Add the value of a specialized variable to the stack string.
5775 */
5776static ssize_t
5777varvalue(char *name, int varflags, int flags)
5778{
5779 int num;
5780 char *p;
5781 int i;
5782 int sep = 0;
5783 int sepq = 0;
5784 ssize_t len = 0;
5785 char **ap;
5786 int syntax;
5787 int quoted = varflags & VSQUOTE;
5788 int subtype = varflags & VSTYPE;
5789 int quotes = flags & (EXP_FULL | EXP_CASE);
5790
5791 if (quoted && (flags & EXP_FULL))
5792 sep = 1 << CHAR_BIT;
5793
5794 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5795 switch (*name) {
5796 case '$':
5797 num = rootpid;
5798 goto numvar;
5799 case '?':
5800 num = exitstatus;
5801 goto numvar;
5802 case '#':
5803 num = shellparam.nparam;
5804 goto numvar;
5805 case '!':
5806 num = backgndpid;
5807 if (num == 0)
5808 return -1;
5809 numvar:
5810 len = cvtnum(num);
5811 break;
5812 case '-':
5813 p = makestrspace(NOPTS, expdest);
5814 for (i = NOPTS - 1; i >= 0; i--) {
5815 if (optlist[i]) {
5816 USTPUTC(optletters(i), p);
5817 len++;
5818 }
5819 }
5820 expdest = p;
5821 break;
5822 case '@':
5823 if (sep)
5824 goto param;
5825 /* fall through */
5826 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005827 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005828 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5829 sepq = 1;
5830 param:
5831 ap = shellparam.p;
5832 if (!ap)
5833 return -1;
5834 while ((p = *ap++)) {
5835 size_t partlen;
5836
5837 partlen = strlen(p);
5838 len += partlen;
5839
5840 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5841 memtodest(p, partlen, syntax, quotes);
5842
5843 if (*ap && sep) {
5844 char *q;
5845
5846 len++;
5847 if (subtype == VSPLUS || subtype == VSLENGTH) {
5848 continue;
5849 }
5850 q = expdest;
5851 if (sepq)
5852 STPUTC(CTLESC, q);
5853 STPUTC(sep, q);
5854 expdest = q;
5855 }
5856 }
5857 return len;
5858 case '0':
5859 case '1':
5860 case '2':
5861 case '3':
5862 case '4':
5863 case '5':
5864 case '6':
5865 case '7':
5866 case '8':
5867 case '9':
5868 num = atoi(name);
5869 if (num < 0 || num > shellparam.nparam)
5870 return -1;
5871 p = num ? shellparam.p[num - 1] : arg0;
5872 goto value;
5873 default:
5874 p = lookupvar(name);
5875 value:
5876 if (!p)
5877 return -1;
5878
5879 len = strlen(p);
5880 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5881 memtodest(p, len, syntax, quotes);
5882 return len;
5883 }
5884
5885 if (subtype == VSPLUS || subtype == VSLENGTH)
5886 STADJUST(-len, expdest);
5887 return len;
5888}
5889
5890/*
5891 * Expand a variable, and return a pointer to the next character in the
5892 * input string.
5893 */
5894static char *
5895evalvar(char *p, int flag)
5896{
5897 int subtype;
5898 int varflags;
5899 char *var;
5900 int patloc;
5901 int c;
5902 int startloc;
5903 ssize_t varlen;
5904 int easy;
5905 int quotes;
5906 int quoted;
5907
5908 quotes = flag & (EXP_FULL | EXP_CASE);
5909 varflags = *p++;
5910 subtype = varflags & VSTYPE;
5911 quoted = varflags & VSQUOTE;
5912 var = p;
5913 easy = (!quoted || (*var == '@' && shellparam.nparam));
5914 startloc = expdest - (char *)stackblock();
5915 p = strchr(p, '=') + 1;
5916
5917 again:
5918 varlen = varvalue(var, varflags, flag);
5919 if (varflags & VSNUL)
5920 varlen--;
5921
5922 if (subtype == VSPLUS) {
5923 varlen = -1 - varlen;
5924 goto vsplus;
5925 }
5926
5927 if (subtype == VSMINUS) {
5928 vsplus:
5929 if (varlen < 0) {
5930 argstr(
5931 p, flag | EXP_TILDE |
5932 (quoted ? EXP_QWORD : EXP_WORD)
5933 );
5934 goto end;
5935 }
5936 if (easy)
5937 goto record;
5938 goto end;
5939 }
5940
5941 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5942 if (varlen < 0) {
5943 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5944 varflags &= ~VSNUL;
5945 /*
5946 * Remove any recorded regions beyond
5947 * start of variable
5948 */
5949 removerecordregions(startloc);
5950 goto again;
5951 }
5952 goto end;
5953 }
5954 if (easy)
5955 goto record;
5956 goto end;
5957 }
5958
5959 if (varlen < 0 && uflag)
5960 varunset(p, var, 0, 0);
5961
5962 if (subtype == VSLENGTH) {
5963 cvtnum(varlen > 0 ? varlen : 0);
5964 goto record;
5965 }
5966
5967 if (subtype == VSNORMAL) {
5968 if (!easy)
5969 goto end;
5970 record:
5971 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5972 goto end;
5973 }
5974
5975#if DEBUG
5976 switch (subtype) {
5977 case VSTRIMLEFT:
5978 case VSTRIMLEFTMAX:
5979 case VSTRIMRIGHT:
5980 case VSTRIMRIGHTMAX:
5981 break;
5982 default:
5983 abort();
5984 }
5985#endif
5986
5987 if (varlen >= 0) {
5988 /*
5989 * Terminate the string and start recording the pattern
5990 * right after it
5991 */
5992 STPUTC('\0', expdest);
5993 patloc = expdest - (char *)stackblock();
5994 if (subevalvar(p, NULL, patloc, subtype,
5995 startloc, varflags, quotes) == 0) {
5996 int amount = expdest - (
5997 (char *)stackblock() + patloc - 1
5998 );
5999 STADJUST(-amount, expdest);
6000 }
6001 /* Remove any recorded regions beyond start of variable */
6002 removerecordregions(startloc);
6003 goto record;
6004 }
6005
6006 end:
6007 if (subtype != VSNORMAL) { /* skip to end of alternative */
6008 int nesting = 1;
6009 for (;;) {
6010 c = *p++;
6011 if (c == CTLESC)
6012 p++;
6013 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6014 if (varlen >= 0)
6015 argbackq = argbackq->next;
6016 } else if (c == CTLVAR) {
6017 if ((*p++ & VSTYPE) != VSNORMAL)
6018 nesting++;
6019 } else if (c == CTLENDVAR) {
6020 if (--nesting == 0)
6021 break;
6022 }
6023 }
6024 }
6025 return p;
6026}
6027
6028/*
6029 * Break the argument string into pieces based upon IFS and add the
6030 * strings to the argument list. The regions of the string to be
6031 * searched for IFS characters have been stored by recordregion.
6032 */
6033static void
6034ifsbreakup(char *string, struct arglist *arglist)
6035{
6036 struct ifsregion *ifsp;
6037 struct strlist *sp;
6038 char *start;
6039 char *p;
6040 char *q;
6041 const char *ifs, *realifs;
6042 int ifsspc;
6043 int nulonly;
6044
6045 start = string;
6046 if (ifslastp != NULL) {
6047 ifsspc = 0;
6048 nulonly = 0;
6049 realifs = ifsset() ? ifsval() : defifs;
6050 ifsp = &ifsfirst;
6051 do {
6052 p = string + ifsp->begoff;
6053 nulonly = ifsp->nulonly;
6054 ifs = nulonly ? nullstr : realifs;
6055 ifsspc = 0;
6056 while (p < string + ifsp->endoff) {
6057 q = p;
6058 if (*p == CTLESC)
6059 p++;
6060 if (!strchr(ifs, *p)) {
6061 p++;
6062 continue;
6063 }
6064 if (!nulonly)
6065 ifsspc = (strchr(defifs, *p) != NULL);
6066 /* Ignore IFS whitespace at start */
6067 if (q == start && ifsspc) {
6068 p++;
6069 start = p;
6070 continue;
6071 }
6072 *q = '\0';
6073 sp = stalloc(sizeof(*sp));
6074 sp->text = start;
6075 *arglist->lastp = sp;
6076 arglist->lastp = &sp->next;
6077 p++;
6078 if (!nulonly) {
6079 for (;;) {
6080 if (p >= string + ifsp->endoff) {
6081 break;
6082 }
6083 q = p;
6084 if (*p == CTLESC)
6085 p++;
6086 if (strchr(ifs, *p) == NULL ) {
6087 p = q;
6088 break;
6089 } else if (strchr(defifs, *p) == NULL) {
6090 if (ifsspc) {
6091 p++;
6092 ifsspc = 0;
6093 } else {
6094 p = q;
6095 break;
6096 }
6097 } else
6098 p++;
6099 }
6100 }
6101 start = p;
6102 } /* while */
6103 ifsp = ifsp->next;
6104 } while (ifsp != NULL);
6105 if (nulonly)
6106 goto add;
6107 }
6108
6109 if (!*start)
6110 return;
6111
6112 add:
6113 sp = stalloc(sizeof(*sp));
6114 sp->text = start;
6115 *arglist->lastp = sp;
6116 arglist->lastp = &sp->next;
6117}
6118
6119static void
6120ifsfree(void)
6121{
6122 struct ifsregion *p;
6123
6124 INT_OFF;
6125 p = ifsfirst.next;
6126 do {
6127 struct ifsregion *ifsp;
6128 ifsp = p->next;
6129 free(p);
6130 p = ifsp;
6131 } while (p);
6132 ifslastp = NULL;
6133 ifsfirst.next = NULL;
6134 INT_ON;
6135}
6136
6137/*
6138 * Add a file name to the list.
6139 */
6140static void
6141addfname(const char *name)
6142{
6143 struct strlist *sp;
6144
6145 sp = stalloc(sizeof(*sp));
6146 sp->text = ststrdup(name);
6147 *exparg.lastp = sp;
6148 exparg.lastp = &sp->next;
6149}
6150
6151static char *expdir;
6152
6153/*
6154 * Do metacharacter (i.e. *, ?, [...]) expansion.
6155 */
6156static void
6157expmeta(char *enddir, char *name)
6158{
6159 char *p;
6160 const char *cp;
6161 char *start;
6162 char *endname;
6163 int metaflag;
6164 struct stat statb;
6165 DIR *dirp;
6166 struct dirent *dp;
6167 int atend;
6168 int matchdot;
6169
6170 metaflag = 0;
6171 start = name;
6172 for (p = name; *p; p++) {
6173 if (*p == '*' || *p == '?')
6174 metaflag = 1;
6175 else if (*p == '[') {
6176 char *q = p + 1;
6177 if (*q == '!')
6178 q++;
6179 for (;;) {
6180 if (*q == '\\')
6181 q++;
6182 if (*q == '/' || *q == '\0')
6183 break;
6184 if (*++q == ']') {
6185 metaflag = 1;
6186 break;
6187 }
6188 }
6189 } else if (*p == '\\')
6190 p++;
6191 else if (*p == '/') {
6192 if (metaflag)
6193 goto out;
6194 start = p + 1;
6195 }
6196 }
6197 out:
6198 if (metaflag == 0) { /* we've reached the end of the file name */
6199 if (enddir != expdir)
6200 metaflag++;
6201 p = name;
6202 do {
6203 if (*p == '\\')
6204 p++;
6205 *enddir++ = *p;
6206 } while (*p++);
6207 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6208 addfname(expdir);
6209 return;
6210 }
6211 endname = p;
6212 if (name < start) {
6213 p = name;
6214 do {
6215 if (*p == '\\')
6216 p++;
6217 *enddir++ = *p++;
6218 } while (p < start);
6219 }
6220 if (enddir == expdir) {
6221 cp = ".";
6222 } else if (enddir == expdir + 1 && *expdir == '/') {
6223 cp = "/";
6224 } else {
6225 cp = expdir;
6226 enddir[-1] = '\0';
6227 }
6228 dirp = opendir(cp);
6229 if (dirp == NULL)
6230 return;
6231 if (enddir != expdir)
6232 enddir[-1] = '/';
6233 if (*endname == 0) {
6234 atend = 1;
6235 } else {
6236 atend = 0;
6237 *endname++ = '\0';
6238 }
6239 matchdot = 0;
6240 p = start;
6241 if (*p == '\\')
6242 p++;
6243 if (*p == '.')
6244 matchdot++;
6245 while (! intpending && (dp = readdir(dirp)) != NULL) {
6246 if (dp->d_name[0] == '.' && ! matchdot)
6247 continue;
6248 if (pmatch(start, dp->d_name)) {
6249 if (atend) {
6250 strcpy(enddir, dp->d_name);
6251 addfname(expdir);
6252 } else {
6253 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6254 continue;
6255 p[-1] = '/';
6256 expmeta(p, endname);
6257 }
6258 }
6259 }
6260 closedir(dirp);
6261 if (! atend)
6262 endname[-1] = '/';
6263}
6264
6265static struct strlist *
6266msort(struct strlist *list, int len)
6267{
6268 struct strlist *p, *q = NULL;
6269 struct strlist **lpp;
6270 int half;
6271 int n;
6272
6273 if (len <= 1)
6274 return list;
6275 half = len >> 1;
6276 p = list;
6277 for (n = half; --n >= 0; ) {
6278 q = p;
6279 p = p->next;
6280 }
6281 q->next = NULL; /* terminate first half of list */
6282 q = msort(list, half); /* sort first half of list */
6283 p = msort(p, len - half); /* sort second half */
6284 lpp = &list;
6285 for (;;) {
6286#if ENABLE_LOCALE_SUPPORT
6287 if (strcoll(p->text, q->text) < 0)
6288#else
6289 if (strcmp(p->text, q->text) < 0)
6290#endif
6291 {
6292 *lpp = p;
6293 lpp = &p->next;
6294 p = *lpp;
6295 if (p == NULL) {
6296 *lpp = q;
6297 break;
6298 }
6299 } else {
6300 *lpp = q;
6301 lpp = &q->next;
6302 q = *lpp;
6303 if (q == NULL) {
6304 *lpp = p;
6305 break;
6306 }
6307 }
6308 }
6309 return list;
6310}
6311
6312/*
6313 * Sort the results of file name expansion. It calculates the number of
6314 * strings to sort and then calls msort (short for merge sort) to do the
6315 * work.
6316 */
6317static struct strlist *
6318expsort(struct strlist *str)
6319{
6320 int len;
6321 struct strlist *sp;
6322
6323 len = 0;
6324 for (sp = str; sp; sp = sp->next)
6325 len++;
6326 return msort(str, len);
6327}
6328
6329static void
6330expandmeta(struct strlist *str, int flag)
6331{
6332 static const char metachars[] = {
6333 '*', '?', '[', 0
6334 };
6335 /* TODO - EXP_REDIR */
6336
6337 while (str) {
6338 struct strlist **savelastp;
6339 struct strlist *sp;
6340 char *p;
6341
6342 if (fflag)
6343 goto nometa;
6344 if (!strpbrk(str->text, metachars))
6345 goto nometa;
6346 savelastp = exparg.lastp;
6347
6348 INT_OFF;
6349 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6350 {
6351 int i = strlen(str->text);
6352 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6353 }
6354
6355 expmeta(expdir, p);
6356 free(expdir);
6357 if (p != str->text)
6358 free(p);
6359 INT_ON;
6360 if (exparg.lastp == savelastp) {
6361 /*
6362 * no matches
6363 */
6364 nometa:
6365 *exparg.lastp = str;
6366 rmescapes(str->text);
6367 exparg.lastp = &str->next;
6368 } else {
6369 *exparg.lastp = NULL;
6370 *savelastp = sp = expsort(*savelastp);
6371 while (sp->next != NULL)
6372 sp = sp->next;
6373 exparg.lastp = &sp->next;
6374 }
6375 str = str->next;
6376 }
6377}
6378
6379/*
6380 * Perform variable substitution and command substitution on an argument,
6381 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6382 * perform splitting and file name expansion. When arglist is NULL, perform
6383 * here document expansion.
6384 */
6385static void
6386expandarg(union node *arg, struct arglist *arglist, int flag)
6387{
6388 struct strlist *sp;
6389 char *p;
6390
6391 argbackq = arg->narg.backquote;
6392 STARTSTACKSTR(expdest);
6393 ifsfirst.next = NULL;
6394 ifslastp = NULL;
6395 argstr(arg->narg.text, flag);
6396 p = _STPUTC('\0', expdest);
6397 expdest = p - 1;
6398 if (arglist == NULL) {
6399 return; /* here document expanded */
6400 }
6401 p = grabstackstr(p);
6402 exparg.lastp = &exparg.list;
6403 /*
6404 * TODO - EXP_REDIR
6405 */
6406 if (flag & EXP_FULL) {
6407 ifsbreakup(p, &exparg);
6408 *exparg.lastp = NULL;
6409 exparg.lastp = &exparg.list;
6410 expandmeta(exparg.list, flag);
6411 } else {
6412 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6413 rmescapes(p);
6414 sp = stalloc(sizeof(*sp));
6415 sp->text = p;
6416 *exparg.lastp = sp;
6417 exparg.lastp = &sp->next;
6418 }
6419 if (ifsfirst.next)
6420 ifsfree();
6421 *exparg.lastp = NULL;
6422 if (exparg.list) {
6423 *arglist->lastp = exparg.list;
6424 arglist->lastp = exparg.lastp;
6425 }
6426}
6427
6428/*
6429 * Expand shell variables and backquotes inside a here document.
6430 */
6431static void
6432expandhere(union node *arg, int fd)
6433{
6434 herefd = fd;
6435 expandarg(arg, (struct arglist *)NULL, 0);
6436 full_write(fd, stackblock(), expdest - (char *)stackblock());
6437}
6438
6439/*
6440 * Returns true if the pattern matches the string.
6441 */
6442static int
6443patmatch(char *pattern, const char *string)
6444{
6445 return pmatch(preglob(pattern, 0, 0), string);
6446}
6447
6448/*
6449 * See if a pattern matches in a case statement.
6450 */
6451static int
6452casematch(union node *pattern, char *val)
6453{
6454 struct stackmark smark;
6455 int result;
6456
6457 setstackmark(&smark);
6458 argbackq = pattern->narg.backquote;
6459 STARTSTACKSTR(expdest);
6460 ifslastp = NULL;
6461 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6462 STACKSTRNUL(expdest);
6463 result = patmatch(stackblock(), val);
6464 popstackmark(&smark);
6465 return result;
6466}
6467
6468
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006469/* ============ find_command */
6470
6471struct builtincmd {
6472 const char *name;
6473 int (*builtin)(int, char **);
6474 /* unsigned flags; */
6475};
6476#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6477#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6478#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6479
6480struct cmdentry {
6481 int cmdtype;
6482 union param {
6483 int index;
6484 const struct builtincmd *cmd;
6485 struct funcnode *func;
6486 } u;
6487};
6488/* values of cmdtype */
6489#define CMDUNKNOWN -1 /* no entry in table for command */
6490#define CMDNORMAL 0 /* command is an executable program */
6491#define CMDFUNCTION 1 /* command is a shell function */
6492#define CMDBUILTIN 2 /* command is a shell builtin */
6493
6494/* action to find_command() */
6495#define DO_ERR 0x01 /* prints errors */
6496#define DO_ABS 0x02 /* checks absolute paths */
6497#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6498#define DO_ALTPATH 0x08 /* using alternate path */
6499#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6500
6501static void find_command(char *, struct cmdentry *, int, const char *);
6502
6503
6504/* ============ Hashing commands */
6505
6506/*
6507 * When commands are first encountered, they are entered in a hash table.
6508 * This ensures that a full path search will not have to be done for them
6509 * on each invocation.
6510 *
6511 * We should investigate converting to a linear search, even though that
6512 * would make the command name "hash" a misnomer.
6513 */
6514
6515#define CMDTABLESIZE 31 /* should be prime */
6516#define ARB 1 /* actual size determined at run time */
6517
6518struct tblentry {
6519 struct tblentry *next; /* next entry in hash chain */
6520 union param param; /* definition of builtin function */
6521 short cmdtype; /* index identifying command */
6522 char rehash; /* if set, cd done since entry created */
6523 char cmdname[ARB]; /* name of command */
6524};
6525
6526static struct tblentry *cmdtable[CMDTABLESIZE];
6527static int builtinloc = -1; /* index in path of %builtin, or -1 */
6528
6529static void
6530tryexec(char *cmd, char **argv, char **envp)
6531{
6532 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006533
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006534#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006535 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006536 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006537
6538 a = find_applet_by_name(cmd);
6539 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006540 if (a->noexec) {
6541 char **c = argv;
6542 while (*c) c++;
6543 current_applet = a;
6544 run_current_applet_and_exit(c - argv, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006545 }
6546 /* re-exec ourselves with the new arguments */
6547 execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
6548 /* If they called chroot or otherwise made the binary no longer
6549 * executable, fall through */
6550 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006551 }
6552#endif
6553
6554 repeat:
6555#ifdef SYSV
6556 do {
6557 execve(cmd, argv, envp);
6558 } while (errno == EINTR);
6559#else
6560 execve(cmd, argv, envp);
6561#endif
6562 if (repeated++) {
6563 free(argv);
6564 } else if (errno == ENOEXEC) {
6565 char **ap;
6566 char **new;
6567
6568 for (ap = argv; *ap; ap++)
6569 ;
6570 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6571 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006572 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006573 ap += 2;
6574 argv++;
6575 while ((*ap++ = *argv++))
6576 ;
6577 argv = new;
6578 goto repeat;
6579 }
6580}
6581
6582/*
6583 * Exec a program. Never returns. If you change this routine, you may
6584 * have to change the find_command routine as well.
6585 */
6586#define environment() listvars(VEXPORT, VUNSET, 0)
6587static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6588static void
6589shellexec(char **argv, const char *path, int idx)
6590{
6591 char *cmdname;
6592 int e;
6593 char **envp;
6594 int exerrno;
6595
6596 clearredir(1);
6597 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006598 if (strchr(argv[0], '/')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006599#if ENABLE_FEATURE_SH_STANDALONE_SHELL
6600 || find_applet_by_name(argv[0])
6601#endif
6602 ) {
6603 tryexec(argv[0], argv, envp);
6604 e = errno;
6605 } else {
6606 e = ENOENT;
6607 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6608 if (--idx < 0 && pathopt == NULL) {
6609 tryexec(cmdname, argv, envp);
6610 if (errno != ENOENT && errno != ENOTDIR)
6611 e = errno;
6612 }
6613 stunalloc(cmdname);
6614 }
6615 }
6616
6617 /* Map to POSIX errors */
6618 switch (e) {
6619 case EACCES:
6620 exerrno = 126;
6621 break;
6622 case ENOENT:
6623 exerrno = 127;
6624 break;
6625 default:
6626 exerrno = 2;
6627 break;
6628 }
6629 exitstatus = exerrno;
6630 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6631 argv[0], e, suppressint ));
6632 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6633 /* NOTREACHED */
6634}
6635
6636static void
6637printentry(struct tblentry *cmdp)
6638{
6639 int idx;
6640 const char *path;
6641 char *name;
6642
6643 idx = cmdp->param.index;
6644 path = pathval();
6645 do {
6646 name = padvance(&path, cmdp->cmdname);
6647 stunalloc(name);
6648 } while (--idx >= 0);
6649 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6650}
6651
6652/*
6653 * Clear out command entries. The argument specifies the first entry in
6654 * PATH which has changed.
6655 */
6656static void
6657clearcmdentry(int firstchange)
6658{
6659 struct tblentry **tblp;
6660 struct tblentry **pp;
6661 struct tblentry *cmdp;
6662
6663 INT_OFF;
6664 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6665 pp = tblp;
6666 while ((cmdp = *pp) != NULL) {
6667 if ((cmdp->cmdtype == CMDNORMAL &&
6668 cmdp->param.index >= firstchange)
6669 || (cmdp->cmdtype == CMDBUILTIN &&
6670 builtinloc >= firstchange)
6671 ) {
6672 *pp = cmdp->next;
6673 free(cmdp);
6674 } else {
6675 pp = &cmdp->next;
6676 }
6677 }
6678 }
6679 INT_ON;
6680}
6681
6682/*
6683 * Locate a command in the command hash table. If "add" is nonzero,
6684 * add the command to the table if it is not already present. The
6685 * variable "lastcmdentry" is set to point to the address of the link
6686 * pointing to the entry, so that delete_cmd_entry can delete the
6687 * entry.
6688 *
6689 * Interrupts must be off if called with add != 0.
6690 */
6691static struct tblentry **lastcmdentry;
6692
6693static struct tblentry *
6694cmdlookup(const char *name, int add)
6695{
6696 unsigned int hashval;
6697 const char *p;
6698 struct tblentry *cmdp;
6699 struct tblentry **pp;
6700
6701 p = name;
6702 hashval = (unsigned char)*p << 4;
6703 while (*p)
6704 hashval += (unsigned char)*p++;
6705 hashval &= 0x7FFF;
6706 pp = &cmdtable[hashval % CMDTABLESIZE];
6707 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6708 if (strcmp(cmdp->cmdname, name) == 0)
6709 break;
6710 pp = &cmdp->next;
6711 }
6712 if (add && cmdp == NULL) {
6713 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6714 + strlen(name) + 1);
6715 cmdp->next = NULL;
6716 cmdp->cmdtype = CMDUNKNOWN;
6717 strcpy(cmdp->cmdname, name);
6718 }
6719 lastcmdentry = pp;
6720 return cmdp;
6721}
6722
6723/*
6724 * Delete the command entry returned on the last lookup.
6725 */
6726static void
6727delete_cmd_entry(void)
6728{
6729 struct tblentry *cmdp;
6730
6731 INT_OFF;
6732 cmdp = *lastcmdentry;
6733 *lastcmdentry = cmdp->next;
6734 if (cmdp->cmdtype == CMDFUNCTION)
6735 freefunc(cmdp->param.func);
6736 free(cmdp);
6737 INT_ON;
6738}
6739
6740/*
6741 * Add a new command entry, replacing any existing command entry for
6742 * the same name - except special builtins.
6743 */
6744static void
6745addcmdentry(char *name, struct cmdentry *entry)
6746{
6747 struct tblentry *cmdp;
6748
6749 cmdp = cmdlookup(name, 1);
6750 if (cmdp->cmdtype == CMDFUNCTION) {
6751 freefunc(cmdp->param.func);
6752 }
6753 cmdp->cmdtype = entry->cmdtype;
6754 cmdp->param = entry->u;
6755 cmdp->rehash = 0;
6756}
6757
6758static int
6759hashcmd(int argc, char **argv)
6760{
6761 struct tblentry **pp;
6762 struct tblentry *cmdp;
6763 int c;
6764 struct cmdentry entry;
6765 char *name;
6766
6767 while ((c = nextopt("r")) != '\0') {
6768 clearcmdentry(0);
6769 return 0;
6770 }
6771 if (*argptr == NULL) {
6772 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6773 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6774 if (cmdp->cmdtype == CMDNORMAL)
6775 printentry(cmdp);
6776 }
6777 }
6778 return 0;
6779 }
6780 c = 0;
6781 while ((name = *argptr) != NULL) {
6782 cmdp = cmdlookup(name, 0);
6783 if (cmdp != NULL
6784 && (cmdp->cmdtype == CMDNORMAL
6785 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6786 delete_cmd_entry();
6787 find_command(name, &entry, DO_ERR, pathval());
6788 if (entry.cmdtype == CMDUNKNOWN)
6789 c = 1;
6790 argptr++;
6791 }
6792 return c;
6793}
6794
6795/*
6796 * Called when a cd is done. Marks all commands so the next time they
6797 * are executed they will be rehashed.
6798 */
6799static void
6800hashcd(void)
6801{
6802 struct tblentry **pp;
6803 struct tblentry *cmdp;
6804
6805 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6806 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6807 if (cmdp->cmdtype == CMDNORMAL || (
6808 cmdp->cmdtype == CMDBUILTIN &&
6809 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6810 builtinloc > 0
6811 ))
6812 cmdp->rehash = 1;
6813 }
6814 }
6815}
6816
6817/*
6818 * Fix command hash table when PATH changed.
6819 * Called before PATH is changed. The argument is the new value of PATH;
6820 * pathval() still returns the old value at this point.
6821 * Called with interrupts off.
6822 */
6823static void
6824changepath(const char *newval)
6825{
6826 const char *old, *new;
6827 int idx;
6828 int firstchange;
6829 int idx_bltin;
6830
6831 old = pathval();
6832 new = newval;
6833 firstchange = 9999; /* assume no change */
6834 idx = 0;
6835 idx_bltin = -1;
6836 for (;;) {
6837 if (*old != *new) {
6838 firstchange = idx;
6839 if ((*old == '\0' && *new == ':')
6840 || (*old == ':' && *new == '\0'))
6841 firstchange++;
6842 old = new; /* ignore subsequent differences */
6843 }
6844 if (*new == '\0')
6845 break;
6846 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6847 idx_bltin = idx;
6848 if (*new == ':') {
6849 idx++;
6850 }
6851 new++, old++;
6852 }
6853 if (builtinloc < 0 && idx_bltin >= 0)
6854 builtinloc = idx_bltin; /* zap builtins */
6855 if (builtinloc >= 0 && idx_bltin < 0)
6856 firstchange = 0;
6857 clearcmdentry(firstchange);
6858 builtinloc = idx_bltin;
6859}
6860
6861#define TEOF 0
6862#define TNL 1
6863#define TREDIR 2
6864#define TWORD 3
6865#define TSEMI 4
6866#define TBACKGND 5
6867#define TAND 6
6868#define TOR 7
6869#define TPIPE 8
6870#define TLP 9
6871#define TRP 10
6872#define TENDCASE 11
6873#define TENDBQUOTE 12
6874#define TNOT 13
6875#define TCASE 14
6876#define TDO 15
6877#define TDONE 16
6878#define TELIF 17
6879#define TELSE 18
6880#define TESAC 19
6881#define TFI 20
6882#define TFOR 21
6883#define TIF 22
6884#define TIN 23
6885#define TTHEN 24
6886#define TUNTIL 25
6887#define TWHILE 26
6888#define TBEGIN 27
6889#define TEND 28
6890
6891/* first char is indicating which tokens mark the end of a list */
6892static const char *const tokname_array[] = {
6893 "\1end of file",
6894 "\0newline",
6895 "\0redirection",
6896 "\0word",
6897 "\0;",
6898 "\0&",
6899 "\0&&",
6900 "\0||",
6901 "\0|",
6902 "\0(",
6903 "\1)",
6904 "\1;;",
6905 "\1`",
6906#define KWDOFFSET 13
6907 /* the following are keywords */
6908 "\0!",
6909 "\0case",
6910 "\1do",
6911 "\1done",
6912 "\1elif",
6913 "\1else",
6914 "\1esac",
6915 "\1fi",
6916 "\0for",
6917 "\0if",
6918 "\0in",
6919 "\1then",
6920 "\0until",
6921 "\0while",
6922 "\0{",
6923 "\1}",
6924};
6925
6926static const char *
6927tokname(int tok)
6928{
6929 static char buf[16];
6930
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006931//try this:
6932//if (tok < TSEMI) return tokname_array[tok] + 1;
6933//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6934//return buf;
6935
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006936 if (tok >= TSEMI)
6937 buf[0] = '"';
6938 sprintf(buf + (tok >= TSEMI), "%s%c",
6939 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6940 return buf;
6941}
6942
6943/* Wrapper around strcmp for qsort/bsearch/... */
6944static int
6945pstrcmp(const void *a, const void *b)
6946{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006947 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006948}
6949
6950static const char *const *
6951findkwd(const char *s)
6952{
6953 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006954 (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
6955 sizeof(char *), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006956}
6957
6958/*
6959 * Locate and print what a word is...
6960 */
6961#if ENABLE_ASH_CMDCMD
6962static int
6963describe_command(char *command, int describe_command_verbose)
6964#else
6965#define describe_command_verbose 1
6966static int
6967describe_command(char *command)
6968#endif
6969{
6970 struct cmdentry entry;
6971 struct tblentry *cmdp;
6972#if ENABLE_ASH_ALIAS
6973 const struct alias *ap;
6974#endif
6975 const char *path = pathval();
6976
6977 if (describe_command_verbose) {
6978 out1str(command);
6979 }
6980
6981 /* First look at the keywords */
6982 if (findkwd(command)) {
6983 out1str(describe_command_verbose ? " is a shell keyword" : command);
6984 goto out;
6985 }
6986
6987#if ENABLE_ASH_ALIAS
6988 /* Then look at the aliases */
6989 ap = lookupalias(command, 0);
6990 if (ap != NULL) {
6991 if (describe_command_verbose) {
6992 out1fmt(" is an alias for %s", ap->val);
6993 } else {
6994 out1str("alias ");
6995 printalias(ap);
6996 return 0;
6997 }
6998 goto out;
6999 }
7000#endif
7001 /* Then check if it is a tracked alias */
7002 cmdp = cmdlookup(command, 0);
7003 if (cmdp != NULL) {
7004 entry.cmdtype = cmdp->cmdtype;
7005 entry.u = cmdp->param;
7006 } else {
7007 /* Finally use brute force */
7008 find_command(command, &entry, DO_ABS, path);
7009 }
7010
7011 switch (entry.cmdtype) {
7012 case CMDNORMAL: {
7013 int j = entry.u.index;
7014 char *p;
7015 if (j == -1) {
7016 p = command;
7017 } else {
7018 do {
7019 p = padvance(&path, command);
7020 stunalloc(p);
7021 } while (--j >= 0);
7022 }
7023 if (describe_command_verbose) {
7024 out1fmt(" is%s %s",
7025 (cmdp ? " a tracked alias for" : nullstr), p
7026 );
7027 } else {
7028 out1str(p);
7029 }
7030 break;
7031 }
7032
7033 case CMDFUNCTION:
7034 if (describe_command_verbose) {
7035 out1str(" is a shell function");
7036 } else {
7037 out1str(command);
7038 }
7039 break;
7040
7041 case CMDBUILTIN:
7042 if (describe_command_verbose) {
7043 out1fmt(" is a %sshell builtin",
7044 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7045 "special " : nullstr
7046 );
7047 } else {
7048 out1str(command);
7049 }
7050 break;
7051
7052 default:
7053 if (describe_command_verbose) {
7054 out1str(": not found\n");
7055 }
7056 return 127;
7057 }
7058 out:
7059 outstr("\n", stdout);
7060 return 0;
7061}
7062
7063static int
7064typecmd(int argc, char **argv)
7065{
7066 int i;
7067 int err = 0;
7068
7069 for (i = 1; i < argc; i++) {
7070#if ENABLE_ASH_CMDCMD
7071 err |= describe_command(argv[i], 1);
7072#else
7073 err |= describe_command(argv[i]);
7074#endif
7075 }
7076 return err;
7077}
7078
7079#if ENABLE_ASH_CMDCMD
7080static int
7081commandcmd(int argc, char **argv)
7082{
7083 int c;
7084 enum {
7085 VERIFY_BRIEF = 1,
7086 VERIFY_VERBOSE = 2,
7087 } verify = 0;
7088
7089 while ((c = nextopt("pvV")) != '\0')
7090 if (c == 'V')
7091 verify |= VERIFY_VERBOSE;
7092 else if (c == 'v')
7093 verify |= VERIFY_BRIEF;
7094#if DEBUG
7095 else if (c != 'p')
7096 abort();
7097#endif
7098 if (verify)
7099 return describe_command(*argptr, verify - VERIFY_BRIEF);
7100
7101 return 0;
7102}
7103#endif
7104
7105
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007106/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007107
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007108static int funcblocksize; /* size of structures in function */
7109static int funcstringsize; /* size of strings in node */
7110static void *funcblock; /* block to allocate function from */
7111static char *funcstring; /* block to allocate strings from */
7112
Eric Andersencb57d552001-06-28 07:25:16 +00007113/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007114#define EV_EXIT 01 /* exit after evaluating tree */
7115#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7116#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007117
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007118static const short nodesize[26] = {
7119 SHELL_ALIGN(sizeof(struct ncmd)),
7120 SHELL_ALIGN(sizeof(struct npipe)),
7121 SHELL_ALIGN(sizeof(struct nredir)),
7122 SHELL_ALIGN(sizeof(struct nredir)),
7123 SHELL_ALIGN(sizeof(struct nredir)),
7124 SHELL_ALIGN(sizeof(struct nbinary)),
7125 SHELL_ALIGN(sizeof(struct nbinary)),
7126 SHELL_ALIGN(sizeof(struct nbinary)),
7127 SHELL_ALIGN(sizeof(struct nif)),
7128 SHELL_ALIGN(sizeof(struct nbinary)),
7129 SHELL_ALIGN(sizeof(struct nbinary)),
7130 SHELL_ALIGN(sizeof(struct nfor)),
7131 SHELL_ALIGN(sizeof(struct ncase)),
7132 SHELL_ALIGN(sizeof(struct nclist)),
7133 SHELL_ALIGN(sizeof(struct narg)),
7134 SHELL_ALIGN(sizeof(struct narg)),
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 nfile)),
7140 SHELL_ALIGN(sizeof(struct ndup)),
7141 SHELL_ALIGN(sizeof(struct ndup)),
7142 SHELL_ALIGN(sizeof(struct nhere)),
7143 SHELL_ALIGN(sizeof(struct nhere)),
7144 SHELL_ALIGN(sizeof(struct nnot)),
7145};
7146
7147static void calcsize(union node *n);
7148
7149static void
7150sizenodelist(struct nodelist *lp)
7151{
7152 while (lp) {
7153 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7154 calcsize(lp->n);
7155 lp = lp->next;
7156 }
7157}
7158
7159static void
7160calcsize(union node *n)
7161{
7162 if (n == NULL)
7163 return;
7164 funcblocksize += nodesize[n->type];
7165 switch (n->type) {
7166 case NCMD:
7167 calcsize(n->ncmd.redirect);
7168 calcsize(n->ncmd.args);
7169 calcsize(n->ncmd.assign);
7170 break;
7171 case NPIPE:
7172 sizenodelist(n->npipe.cmdlist);
7173 break;
7174 case NREDIR:
7175 case NBACKGND:
7176 case NSUBSHELL:
7177 calcsize(n->nredir.redirect);
7178 calcsize(n->nredir.n);
7179 break;
7180 case NAND:
7181 case NOR:
7182 case NSEMI:
7183 case NWHILE:
7184 case NUNTIL:
7185 calcsize(n->nbinary.ch2);
7186 calcsize(n->nbinary.ch1);
7187 break;
7188 case NIF:
7189 calcsize(n->nif.elsepart);
7190 calcsize(n->nif.ifpart);
7191 calcsize(n->nif.test);
7192 break;
7193 case NFOR:
7194 funcstringsize += strlen(n->nfor.var) + 1;
7195 calcsize(n->nfor.body);
7196 calcsize(n->nfor.args);
7197 break;
7198 case NCASE:
7199 calcsize(n->ncase.cases);
7200 calcsize(n->ncase.expr);
7201 break;
7202 case NCLIST:
7203 calcsize(n->nclist.body);
7204 calcsize(n->nclist.pattern);
7205 calcsize(n->nclist.next);
7206 break;
7207 case NDEFUN:
7208 case NARG:
7209 sizenodelist(n->narg.backquote);
7210 funcstringsize += strlen(n->narg.text) + 1;
7211 calcsize(n->narg.next);
7212 break;
7213 case NTO:
7214 case NCLOBBER:
7215 case NFROM:
7216 case NFROMTO:
7217 case NAPPEND:
7218 calcsize(n->nfile.fname);
7219 calcsize(n->nfile.next);
7220 break;
7221 case NTOFD:
7222 case NFROMFD:
7223 calcsize(n->ndup.vname);
7224 calcsize(n->ndup.next);
7225 break;
7226 case NHERE:
7227 case NXHERE:
7228 calcsize(n->nhere.doc);
7229 calcsize(n->nhere.next);
7230 break;
7231 case NNOT:
7232 calcsize(n->nnot.com);
7233 break;
7234 };
7235}
7236
7237static char *
7238nodeckstrdup(char *s)
7239{
7240 char *rtn = funcstring;
7241
7242 strcpy(funcstring, s);
7243 funcstring += strlen(s) + 1;
7244 return rtn;
7245}
7246
7247static union node *copynode(union node *);
7248
7249static struct nodelist *
7250copynodelist(struct nodelist *lp)
7251{
7252 struct nodelist *start;
7253 struct nodelist **lpp;
7254
7255 lpp = &start;
7256 while (lp) {
7257 *lpp = funcblock;
7258 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7259 (*lpp)->n = copynode(lp->n);
7260 lp = lp->next;
7261 lpp = &(*lpp)->next;
7262 }
7263 *lpp = NULL;
7264 return start;
7265}
7266
7267static union node *
7268copynode(union node *n)
7269{
7270 union node *new;
7271
7272 if (n == NULL)
7273 return NULL;
7274 new = funcblock;
7275 funcblock = (char *) funcblock + nodesize[n->type];
7276
7277 switch (n->type) {
7278 case NCMD:
7279 new->ncmd.redirect = copynode(n->ncmd.redirect);
7280 new->ncmd.args = copynode(n->ncmd.args);
7281 new->ncmd.assign = copynode(n->ncmd.assign);
7282 break;
7283 case NPIPE:
7284 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7285 new->npipe.backgnd = n->npipe.backgnd;
7286 break;
7287 case NREDIR:
7288 case NBACKGND:
7289 case NSUBSHELL:
7290 new->nredir.redirect = copynode(n->nredir.redirect);
7291 new->nredir.n = copynode(n->nredir.n);
7292 break;
7293 case NAND:
7294 case NOR:
7295 case NSEMI:
7296 case NWHILE:
7297 case NUNTIL:
7298 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7299 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7300 break;
7301 case NIF:
7302 new->nif.elsepart = copynode(n->nif.elsepart);
7303 new->nif.ifpart = copynode(n->nif.ifpart);
7304 new->nif.test = copynode(n->nif.test);
7305 break;
7306 case NFOR:
7307 new->nfor.var = nodeckstrdup(n->nfor.var);
7308 new->nfor.body = copynode(n->nfor.body);
7309 new->nfor.args = copynode(n->nfor.args);
7310 break;
7311 case NCASE:
7312 new->ncase.cases = copynode(n->ncase.cases);
7313 new->ncase.expr = copynode(n->ncase.expr);
7314 break;
7315 case NCLIST:
7316 new->nclist.body = copynode(n->nclist.body);
7317 new->nclist.pattern = copynode(n->nclist.pattern);
7318 new->nclist.next = copynode(n->nclist.next);
7319 break;
7320 case NDEFUN:
7321 case NARG:
7322 new->narg.backquote = copynodelist(n->narg.backquote);
7323 new->narg.text = nodeckstrdup(n->narg.text);
7324 new->narg.next = copynode(n->narg.next);
7325 break;
7326 case NTO:
7327 case NCLOBBER:
7328 case NFROM:
7329 case NFROMTO:
7330 case NAPPEND:
7331 new->nfile.fname = copynode(n->nfile.fname);
7332 new->nfile.fd = n->nfile.fd;
7333 new->nfile.next = copynode(n->nfile.next);
7334 break;
7335 case NTOFD:
7336 case NFROMFD:
7337 new->ndup.vname = copynode(n->ndup.vname);
7338 new->ndup.dupfd = n->ndup.dupfd;
7339 new->ndup.fd = n->ndup.fd;
7340 new->ndup.next = copynode(n->ndup.next);
7341 break;
7342 case NHERE:
7343 case NXHERE:
7344 new->nhere.doc = copynode(n->nhere.doc);
7345 new->nhere.fd = n->nhere.fd;
7346 new->nhere.next = copynode(n->nhere.next);
7347 break;
7348 case NNOT:
7349 new->nnot.com = copynode(n->nnot.com);
7350 break;
7351 };
7352 new->type = n->type;
7353 return new;
7354}
7355
7356/*
7357 * Make a copy of a parse tree.
7358 */
7359static struct funcnode *
7360copyfunc(union node *n)
7361{
7362 struct funcnode *f;
7363 size_t blocksize;
7364
7365 funcblocksize = offsetof(struct funcnode, n);
7366 funcstringsize = 0;
7367 calcsize(n);
7368 blocksize = funcblocksize;
7369 f = ckmalloc(blocksize + funcstringsize);
7370 funcblock = (char *) f + offsetof(struct funcnode, n);
7371 funcstring = (char *) f + blocksize;
7372 copynode(n);
7373 f->count = 0;
7374 return f;
7375}
7376
7377/*
7378 * Define a shell function.
7379 */
7380static void
7381defun(char *name, union node *func)
7382{
7383 struct cmdentry entry;
7384
7385 INT_OFF;
7386 entry.cmdtype = CMDFUNCTION;
7387 entry.u.func = copyfunc(func);
7388 addcmdentry(name, &entry);
7389 INT_ON;
7390}
7391
7392static int evalskip; /* set if we are skipping commands */
7393/* reasons for skipping commands (see comment on breakcmd routine) */
7394#define SKIPBREAK (1 << 0)
7395#define SKIPCONT (1 << 1)
7396#define SKIPFUNC (1 << 2)
7397#define SKIPFILE (1 << 3)
7398#define SKIPEVAL (1 << 4)
7399static int skipcount; /* number of levels to skip */
7400static int funcnest; /* depth of function calls */
7401
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007402/* forward decl way out to parsing code - dotrap needs it */
7403static int evalstring(char *s, int mask);
7404
7405/*
7406 * Called to execute a trap. Perhaps we should avoid entering new trap
7407 * handlers while we are executing a trap handler.
7408 */
7409static int
7410dotrap(void)
7411{
7412 char *p;
7413 char *q;
7414 int i;
7415 int savestatus;
7416 int skip = 0;
7417
7418 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007419 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007420 xbarrier();
7421
7422 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7423 if (!*q)
7424 continue;
7425 *q = '\0';
7426
7427 p = trap[i + 1];
7428 if (!p)
7429 continue;
7430 skip = evalstring(p, SKIPEVAL);
7431 exitstatus = savestatus;
7432 if (skip)
7433 break;
7434 }
7435
7436 return skip;
7437}
7438
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007439/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007440static void evalloop(union node *, int);
7441static void evalfor(union node *, int);
7442static void evalcase(union node *, int);
7443static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007444static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007445static void evalpipe(union node *, int);
7446static void evalcommand(union node *, int);
7447static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007448static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007449
Eric Andersen62483552001-07-10 06:09:16 +00007450/*
Eric Andersenc470f442003-07-28 09:56:35 +00007451 * Evaluate a parse tree. The value is left in the global variable
7452 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007453 */
Eric Andersenc470f442003-07-28 09:56:35 +00007454static void
7455evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007456{
Eric Andersenc470f442003-07-28 09:56:35 +00007457 int checkexit = 0;
7458 void (*evalfn)(union node *, int);
7459 unsigned isor;
7460 int status;
7461 if (n == NULL) {
7462 TRACE(("evaltree(NULL) called\n"));
7463 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007464 }
Eric Andersenc470f442003-07-28 09:56:35 +00007465 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007466 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007467 switch (n->type) {
7468 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007469#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007470 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007471 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007472 break;
7473#endif
7474 case NNOT:
7475 evaltree(n->nnot.com, EV_TESTED);
7476 status = !exitstatus;
7477 goto setstatus;
7478 case NREDIR:
7479 expredir(n->nredir.redirect);
7480 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7481 if (!status) {
7482 evaltree(n->nredir.n, flags & EV_TESTED);
7483 status = exitstatus;
7484 }
7485 popredir(0);
7486 goto setstatus;
7487 case NCMD:
7488 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007489 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007490 if (eflag && !(flags & EV_TESTED))
7491 checkexit = ~0;
7492 goto calleval;
7493 case NFOR:
7494 evalfn = evalfor;
7495 goto calleval;
7496 case NWHILE:
7497 case NUNTIL:
7498 evalfn = evalloop;
7499 goto calleval;
7500 case NSUBSHELL:
7501 case NBACKGND:
7502 evalfn = evalsubshell;
7503 goto calleval;
7504 case NPIPE:
7505 evalfn = evalpipe;
7506 goto checkexit;
7507 case NCASE:
7508 evalfn = evalcase;
7509 goto calleval;
7510 case NAND:
7511 case NOR:
7512 case NSEMI:
7513#if NAND + 1 != NOR
7514#error NAND + 1 != NOR
7515#endif
7516#if NOR + 1 != NSEMI
7517#error NOR + 1 != NSEMI
7518#endif
7519 isor = n->type - NAND;
7520 evaltree(
7521 n->nbinary.ch1,
7522 (flags | ((isor >> 1) - 1)) & EV_TESTED
7523 );
7524 if (!exitstatus == isor)
7525 break;
7526 if (!evalskip) {
7527 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007528 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007529 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007530 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007531 evalfn(n, flags);
7532 break;
7533 }
7534 break;
7535 case NIF:
7536 evaltree(n->nif.test, EV_TESTED);
7537 if (evalskip)
7538 break;
7539 if (exitstatus == 0) {
7540 n = n->nif.ifpart;
7541 goto evaln;
7542 } else if (n->nif.elsepart) {
7543 n = n->nif.elsepart;
7544 goto evaln;
7545 }
7546 goto success;
7547 case NDEFUN:
7548 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007549 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007550 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007551 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007552 exitstatus = status;
7553 break;
7554 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007555 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007556 if ((checkexit & exitstatus))
7557 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007558 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007559 goto exexit;
7560
7561 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007562 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007563 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007564 }
Eric Andersen62483552001-07-10 06:09:16 +00007565}
7566
Eric Andersenc470f442003-07-28 09:56:35 +00007567#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7568static
7569#endif
7570void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7571
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007572static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007573
7574static void
7575evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007576{
7577 int status;
7578
7579 loopnest++;
7580 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007581 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007582 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007583 int i;
7584
Eric Andersencb57d552001-06-28 07:25:16 +00007585 evaltree(n->nbinary.ch1, EV_TESTED);
7586 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007587 skipping:
7588 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007589 evalskip = 0;
7590 continue;
7591 }
7592 if (evalskip == SKIPBREAK && --skipcount <= 0)
7593 evalskip = 0;
7594 break;
7595 }
Eric Andersenc470f442003-07-28 09:56:35 +00007596 i = exitstatus;
7597 if (n->type != NWHILE)
7598 i = !i;
7599 if (i != 0)
7600 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007601 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007602 status = exitstatus;
7603 if (evalskip)
7604 goto skipping;
7605 }
7606 loopnest--;
7607 exitstatus = status;
7608}
7609
Eric Andersenc470f442003-07-28 09:56:35 +00007610static void
7611evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007612{
7613 struct arglist arglist;
7614 union node *argp;
7615 struct strlist *sp;
7616 struct stackmark smark;
7617
7618 setstackmark(&smark);
7619 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007620 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007621 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007622 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007623 if (evalskip)
7624 goto out;
7625 }
7626 *arglist.lastp = NULL;
7627
7628 exitstatus = 0;
7629 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007630 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007631 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007632 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007633 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007634 if (evalskip) {
7635 if (evalskip == SKIPCONT && --skipcount <= 0) {
7636 evalskip = 0;
7637 continue;
7638 }
7639 if (evalskip == SKIPBREAK && --skipcount <= 0)
7640 evalskip = 0;
7641 break;
7642 }
7643 }
7644 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007645 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007646 popstackmark(&smark);
7647}
7648
Eric Andersenc470f442003-07-28 09:56:35 +00007649static void
7650evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007651{
7652 union node *cp;
7653 union node *patp;
7654 struct arglist arglist;
7655 struct stackmark smark;
7656
7657 setstackmark(&smark);
7658 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007659 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007660 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007661 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7662 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007663 if (casematch(patp, arglist.list->text)) {
7664 if (evalskip == 0) {
7665 evaltree(cp->nclist.body, flags);
7666 }
7667 goto out;
7668 }
7669 }
7670 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007671 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007672 popstackmark(&smark);
7673}
7674
Eric Andersenc470f442003-07-28 09:56:35 +00007675/*
7676 * Kick off a subshell to evaluate a tree.
7677 */
Eric Andersenc470f442003-07-28 09:56:35 +00007678static void
7679evalsubshell(union node *n, int flags)
7680{
7681 struct job *jp;
7682 int backgnd = (n->type == NBACKGND);
7683 int status;
7684
7685 expredir(n->nredir.redirect);
7686 if (!backgnd && flags & EV_EXIT && !trap[0])
7687 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007688 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007689 jp = makejob(n, 1);
7690 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007691 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007692 flags |= EV_EXIT;
7693 if (backgnd)
7694 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007695 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007696 redirect(n->nredir.redirect, 0);
7697 evaltreenr(n->nredir.n, flags);
7698 /* never returns */
7699 }
7700 status = 0;
7701 if (! backgnd)
7702 status = waitforjob(jp);
7703 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007704 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007705}
7706
Eric Andersenc470f442003-07-28 09:56:35 +00007707/*
7708 * Compute the names of the files in a redirection list.
7709 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007710static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007711static void
7712expredir(union node *n)
7713{
7714 union node *redir;
7715
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007716 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007717 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007718
7719 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007720 fn.lastp = &fn.list;
7721 switch (redir->type) {
7722 case NFROMTO:
7723 case NFROM:
7724 case NTO:
7725 case NCLOBBER:
7726 case NAPPEND:
7727 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7728 redir->nfile.expfname = fn.list->text;
7729 break;
7730 case NFROMFD:
7731 case NTOFD:
7732 if (redir->ndup.vname) {
7733 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007734 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007735 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007736 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007737 }
7738 break;
7739 }
7740 }
7741}
7742
Eric Andersencb57d552001-06-28 07:25:16 +00007743/*
Eric Andersencb57d552001-06-28 07:25:16 +00007744 * Evaluate a pipeline. All the processes in the pipeline are children
7745 * of the process creating the pipeline. (This differs from some versions
7746 * of the shell, which make the last process in a pipeline the parent
7747 * of all the rest.)
7748 */
Eric Andersenc470f442003-07-28 09:56:35 +00007749static void
7750evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007751{
7752 struct job *jp;
7753 struct nodelist *lp;
7754 int pipelen;
7755 int prevfd;
7756 int pip[2];
7757
Eric Andersenc470f442003-07-28 09:56:35 +00007758 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007759 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007760 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007761 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007762 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007763 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007764 jp = makejob(n, pipelen);
7765 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007766 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007767 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007768 pip[1] = -1;
7769 if (lp->next) {
7770 if (pipe(pip) < 0) {
7771 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007772 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007773 }
7774 }
7775 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007776 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007777 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007778 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007779 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007780 if (prevfd > 0) {
7781 dup2(prevfd, 0);
7782 close(prevfd);
7783 }
7784 if (pip[1] > 1) {
7785 dup2(pip[1], 1);
7786 close(pip[1]);
7787 }
Eric Andersenc470f442003-07-28 09:56:35 +00007788 evaltreenr(lp->n, flags);
7789 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007790 }
7791 if (prevfd >= 0)
7792 close(prevfd);
7793 prevfd = pip[0];
7794 close(pip[1]);
7795 }
Eric Andersencb57d552001-06-28 07:25:16 +00007796 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007797 exitstatus = waitforjob(jp);
7798 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007799 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007800 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007801}
7802
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007803/*
7804 * Controls whether the shell is interactive or not.
7805 */
7806static void
7807setinteractive(int on)
7808{
7809 static int is_interactive;
7810
7811 if (++on == is_interactive)
7812 return;
7813 is_interactive = on;
7814 setsignal(SIGINT);
7815 setsignal(SIGQUIT);
7816 setsignal(SIGTERM);
7817#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7818 if (is_interactive > 1) {
7819 /* Looks like they want an interactive shell */
7820 static smallint do_banner;
7821
7822 if (!do_banner) {
7823 out1fmt(
7824 "\n\n"
7825 "%s Built-in shell (ash)\n"
7826 "Enter 'help' for a list of built-in commands."
7827 "\n\n",
7828 BB_BANNER);
7829 do_banner = 1;
7830 }
7831 }
7832#endif
7833}
7834
7835#if ENABLE_FEATURE_EDITING_VI
7836#define setvimode(on) do { \
7837 if (on) line_input_state->flags |= VI_MODE; \
7838 else line_input_state->flags &= ~VI_MODE; \
7839} while (0)
7840#else
7841#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7842#endif
7843
7844static void
7845optschanged(void)
7846{
7847#if DEBUG
7848 opentrace();
7849#endif
7850 setinteractive(iflag);
7851 setjobctl(mflag);
7852 setvimode(viflag);
7853}
7854
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007855static struct localvar *localvars;
7856
7857/*
7858 * Called after a function returns.
7859 * Interrupts must be off.
7860 */
7861static void
7862poplocalvars(void)
7863{
7864 struct localvar *lvp;
7865 struct var *vp;
7866
7867 while ((lvp = localvars) != NULL) {
7868 localvars = lvp->next;
7869 vp = lvp->vp;
7870 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7871 if (vp == NULL) { /* $- saved */
7872 memcpy(optlist, lvp->text, sizeof(optlist));
7873 free((char*)lvp->text);
7874 optschanged();
7875 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7876 unsetvar(vp->text);
7877 } else {
7878 if (vp->func)
7879 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7880 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7881 free((char*)vp->text);
7882 vp->flags = lvp->flags;
7883 vp->text = lvp->text;
7884 }
7885 free(lvp);
7886 }
7887}
7888
7889static int
7890evalfun(struct funcnode *func, int argc, char **argv, int flags)
7891{
7892 volatile struct shparam saveparam;
7893 struct localvar *volatile savelocalvars;
7894 struct jmploc *volatile savehandler;
7895 struct jmploc jmploc;
7896 int e;
7897
7898 saveparam = shellparam;
7899 savelocalvars = localvars;
7900 e = setjmp(jmploc.loc);
7901 if (e) {
7902 goto funcdone;
7903 }
7904 INT_OFF;
7905 savehandler = exception_handler;
7906 exception_handler = &jmploc;
7907 localvars = NULL;
7908 shellparam.malloc = 0;
7909 func->count++;
7910 funcnest++;
7911 INT_ON;
7912 shellparam.nparam = argc - 1;
7913 shellparam.p = argv + 1;
7914#if ENABLE_ASH_GETOPTS
7915 shellparam.optind = 1;
7916 shellparam.optoff = -1;
7917#endif
7918 evaltree(&func->n, flags & EV_TESTED);
7919funcdone:
7920 INT_OFF;
7921 funcnest--;
7922 freefunc(func);
7923 poplocalvars();
7924 localvars = savelocalvars;
7925 freeparam(&shellparam);
7926 shellparam = saveparam;
7927 exception_handler = savehandler;
7928 INT_ON;
7929 evalskip &= ~SKIPFUNC;
7930 return e;
7931}
7932
Denis Vlasenko131ae172007-02-18 13:00:19 +00007933#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007934static char **
7935parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007936{
7937 char *cp, c;
7938
7939 for (;;) {
7940 cp = *++argv;
7941 if (!cp)
7942 return 0;
7943 if (*cp++ != '-')
7944 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007945 c = *cp++;
7946 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007947 break;
7948 if (c == '-' && !*cp) {
7949 argv++;
7950 break;
7951 }
7952 do {
7953 switch (c) {
7954 case 'p':
7955 *path = defpath;
7956 break;
7957 default:
7958 /* run 'typecmd' for other options */
7959 return 0;
7960 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007961 c = *cp++;
7962 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007963 }
7964 return argv;
7965}
7966#endif
7967
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007968/*
7969 * Make a variable a local variable. When a variable is made local, it's
7970 * value and flags are saved in a localvar structure. The saved values
7971 * will be restored when the shell function returns. We handle the name
7972 * "-" as a special case.
7973 */
7974static void
7975mklocal(char *name)
7976{
7977 struct localvar *lvp;
7978 struct var **vpp;
7979 struct var *vp;
7980
7981 INT_OFF;
7982 lvp = ckmalloc(sizeof(struct localvar));
7983 if (LONE_DASH(name)) {
7984 char *p;
7985 p = ckmalloc(sizeof(optlist));
7986 lvp->text = memcpy(p, optlist, sizeof(optlist));
7987 vp = NULL;
7988 } else {
7989 char *eq;
7990
7991 vpp = hashvar(name);
7992 vp = *findvar(vpp, name);
7993 eq = strchr(name, '=');
7994 if (vp == NULL) {
7995 if (eq)
7996 setvareq(name, VSTRFIXED);
7997 else
7998 setvar(name, NULL, VSTRFIXED);
7999 vp = *vpp; /* the new variable */
8000 lvp->flags = VUNSET;
8001 } else {
8002 lvp->text = vp->text;
8003 lvp->flags = vp->flags;
8004 vp->flags |= VSTRFIXED|VTEXTFIXED;
8005 if (eq)
8006 setvareq(name, 0);
8007 }
8008 }
8009 lvp->vp = vp;
8010 lvp->next = localvars;
8011 localvars = lvp;
8012 INT_ON;
8013}
8014
8015/*
8016 * The "local" command.
8017 */
8018static int
8019localcmd(int argc, char **argv)
8020{
8021 char *name;
8022
8023 argv = argptr;
8024 while ((name = *argv++) != NULL) {
8025 mklocal(name);
8026 }
8027 return 0;
8028}
8029
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008030static int
8031falsecmd(int argc, char **argv)
8032{
8033 return 1;
8034}
8035
8036static int
8037truecmd(int argc, char **argv)
8038{
8039 return 0;
8040}
8041
8042static int
8043execcmd(int argc, char **argv)
8044{
8045 if (argc > 1) {
8046 iflag = 0; /* exit on error */
8047 mflag = 0;
8048 optschanged();
8049 shellexec(argv + 1, pathval(), 0);
8050 }
8051 return 0;
8052}
8053
8054/*
8055 * The return command.
8056 */
8057static int
8058returncmd(int argc, char **argv)
8059{
8060 /*
8061 * If called outside a function, do what ksh does;
8062 * skip the rest of the file.
8063 */
8064 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8065 return argv[1] ? number(argv[1]) : exitstatus;
8066}
8067
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008068/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008069static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008070static int dotcmd(int, char **);
8071static int evalcmd(int, char **);
8072#if ENABLE_ASH_BUILTIN_ECHO
8073static int echocmd(int, char **);
8074#endif
8075#if ENABLE_ASH_BUILTIN_TEST
8076static int testcmd(int, char **);
8077#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008078static int exitcmd(int, char **);
8079static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008080#if ENABLE_ASH_GETOPTS
8081static int getoptscmd(int, char **);
8082#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008083#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8084static int helpcmd(int argc, char **argv);
8085#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008086#if ENABLE_ASH_MATH_SUPPORT
8087static int letcmd(int, char **);
8088#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008089static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008090static int setcmd(int, char **);
8091static int shiftcmd(int, char **);
8092static int timescmd(int, char **);
8093static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008094static int umaskcmd(int, char **);
8095static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008096static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008097
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008098#define BUILTIN_NOSPEC "0"
8099#define BUILTIN_SPECIAL "1"
8100#define BUILTIN_REGULAR "2"
8101#define BUILTIN_SPEC_REG "3"
8102#define BUILTIN_ASSIGN "4"
8103#define BUILTIN_SPEC_ASSG "5"
8104#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008105#define BUILTIN_SPEC_REG_ASSG "7"
8106
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008107/* make sure to keep these in proper order since it is searched via bsearch() */
8108static const struct builtincmd builtintab[] = {
8109 { BUILTIN_SPEC_REG ".", dotcmd },
8110 { BUILTIN_SPEC_REG ":", truecmd },
8111#if ENABLE_ASH_BUILTIN_TEST
8112 { BUILTIN_REGULAR "[", testcmd },
8113 { BUILTIN_REGULAR "[[", testcmd },
8114#endif
8115#if ENABLE_ASH_ALIAS
8116 { BUILTIN_REG_ASSG "alias", aliascmd },
8117#endif
8118#if JOBS
8119 { BUILTIN_REGULAR "bg", fg_bgcmd },
8120#endif
8121 { BUILTIN_SPEC_REG "break", breakcmd },
8122 { BUILTIN_REGULAR "cd", cdcmd },
8123 { BUILTIN_NOSPEC "chdir", cdcmd },
8124#if ENABLE_ASH_CMDCMD
8125 { BUILTIN_REGULAR "command", commandcmd },
8126#endif
8127 { BUILTIN_SPEC_REG "continue", breakcmd },
8128#if ENABLE_ASH_BUILTIN_ECHO
8129 { BUILTIN_REGULAR "echo", echocmd },
8130#endif
8131 { BUILTIN_SPEC_REG "eval", evalcmd },
8132 { BUILTIN_SPEC_REG "exec", execcmd },
8133 { BUILTIN_SPEC_REG "exit", exitcmd },
8134 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8135 { BUILTIN_REGULAR "false", falsecmd },
8136#if JOBS
8137 { BUILTIN_REGULAR "fg", fg_bgcmd },
8138#endif
8139#if ENABLE_ASH_GETOPTS
8140 { BUILTIN_REGULAR "getopts", getoptscmd },
8141#endif
8142 { BUILTIN_NOSPEC "hash", hashcmd },
8143#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8144 { BUILTIN_NOSPEC "help", helpcmd },
8145#endif
8146#if JOBS
8147 { BUILTIN_REGULAR "jobs", jobscmd },
8148 { BUILTIN_REGULAR "kill", killcmd },
8149#endif
8150#if ENABLE_ASH_MATH_SUPPORT
8151 { BUILTIN_NOSPEC "let", letcmd },
8152#endif
8153 { BUILTIN_ASSIGN "local", localcmd },
8154 { BUILTIN_NOSPEC "pwd", pwdcmd },
8155 { BUILTIN_REGULAR "read", readcmd },
8156 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8157 { BUILTIN_SPEC_REG "return", returncmd },
8158 { BUILTIN_SPEC_REG "set", setcmd },
8159 { BUILTIN_SPEC_REG "shift", shiftcmd },
8160 { BUILTIN_SPEC_REG "source", dotcmd },
8161#if ENABLE_ASH_BUILTIN_TEST
8162 { BUILTIN_REGULAR "test", testcmd },
8163#endif
8164 { BUILTIN_SPEC_REG "times", timescmd },
8165 { BUILTIN_SPEC_REG "trap", trapcmd },
8166 { BUILTIN_REGULAR "true", truecmd },
8167 { BUILTIN_NOSPEC "type", typecmd },
8168 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8169 { BUILTIN_REGULAR "umask", umaskcmd },
8170#if ENABLE_ASH_ALIAS
8171 { BUILTIN_REGULAR "unalias", unaliascmd },
8172#endif
8173 { BUILTIN_SPEC_REG "unset", unsetcmd },
8174 { BUILTIN_REGULAR "wait", waitcmd },
8175};
8176
8177#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
8178
8179#define COMMANDCMD (builtintab + 5 + \
8180 2 * ENABLE_ASH_BUILTIN_TEST + \
8181 ENABLE_ASH_ALIAS + \
8182 ENABLE_ASH_JOB_CONTROL)
8183#define EXECCMD (builtintab + 7 + \
8184 2 * ENABLE_ASH_BUILTIN_TEST + \
8185 ENABLE_ASH_ALIAS + \
8186 ENABLE_ASH_JOB_CONTROL + \
8187 ENABLE_ASH_CMDCMD + \
8188 ENABLE_ASH_BUILTIN_ECHO)
8189
8190/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008191 * Search the table of builtin commands.
8192 */
8193static struct builtincmd *
8194find_builtin(const char *name)
8195{
8196 struct builtincmd *bp;
8197
8198 bp = bsearch(
8199 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
8200 pstrcmp
8201 );
8202 return bp;
8203}
8204
8205/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008206 * Execute a simple command.
8207 */
8208static int back_exitstatus; /* exit status of backquoted command */
8209static int
8210isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008211{
8212 const char *q = endofname(p);
8213 if (p == q)
8214 return 0;
8215 return *q == '=';
8216}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008217static int
8218bltincmd(int argc, char **argv)
8219{
8220 /* Preserve exitstatus of a previous possible redirection
8221 * as POSIX mandates */
8222 return back_exitstatus;
8223}
Eric Andersenc470f442003-07-28 09:56:35 +00008224static void
8225evalcommand(union node *cmd, int flags)
8226{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008227 static const struct builtincmd bltin = {
8228 "\0\0", bltincmd
8229 };
Eric Andersenc470f442003-07-28 09:56:35 +00008230 struct stackmark smark;
8231 union node *argp;
8232 struct arglist arglist;
8233 struct arglist varlist;
8234 char **argv;
8235 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008236 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008237 struct cmdentry cmdentry;
8238 struct job *jp;
8239 char *lastarg;
8240 const char *path;
8241 int spclbltin;
8242 int cmd_is_exec;
8243 int status;
8244 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008245 struct builtincmd *bcmd;
8246 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008247
8248 /* First expand the arguments. */
8249 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8250 setstackmark(&smark);
8251 back_exitstatus = 0;
8252
8253 cmdentry.cmdtype = CMDBUILTIN;
8254 cmdentry.u.cmd = &bltin;
8255 varlist.lastp = &varlist.list;
8256 *varlist.lastp = NULL;
8257 arglist.lastp = &arglist.list;
8258 *arglist.lastp = NULL;
8259
8260 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008261 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008262 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8263 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8264 }
8265
Eric Andersenc470f442003-07-28 09:56:35 +00008266 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8267 struct strlist **spp;
8268
8269 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008270 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008271 expandarg(argp, &arglist, EXP_VARTILDE);
8272 else
8273 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8274
Eric Andersenc470f442003-07-28 09:56:35 +00008275 for (sp = *spp; sp; sp = sp->next)
8276 argc++;
8277 }
8278
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008279 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008280 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008281 TRACE(("evalcommand arg: %s\n", sp->text));
8282 *nargv++ = sp->text;
8283 }
8284 *nargv = NULL;
8285
8286 lastarg = NULL;
8287 if (iflag && funcnest == 0 && argc > 0)
8288 lastarg = nargv[-1];
8289
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008290 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008291 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008292 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008293
8294 path = vpath.text;
8295 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8296 struct strlist **spp;
8297 char *p;
8298
8299 spp = varlist.lastp;
8300 expandarg(argp, &varlist, EXP_VARTILDE);
8301
8302 /*
8303 * Modify the command lookup path, if a PATH= assignment
8304 * is present
8305 */
8306 p = (*spp)->text;
8307 if (varequal(p, path))
8308 path = p;
8309 }
8310
8311 /* Print the command if xflag is set. */
8312 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008313 int n;
8314 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008315
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008316 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008317 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008318
8319 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008320 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008321 while (sp) {
8322 dprintf(preverrout_fd, p, sp->text);
8323 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008324 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008325 p--;
8326 }
8327 }
8328 sp = arglist.list;
8329 }
Rob Landley53437472006-07-16 08:14:35 +00008330 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008331 }
8332
8333 cmd_is_exec = 0;
8334 spclbltin = -1;
8335
8336 /* Now locate the command. */
8337 if (argc) {
8338 const char *oldpath;
8339 int cmd_flag = DO_ERR;
8340
8341 path += 5;
8342 oldpath = path;
8343 for (;;) {
8344 find_command(argv[0], &cmdentry, cmd_flag, path);
8345 if (cmdentry.cmdtype == CMDUNKNOWN) {
8346 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008347 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008348 goto bail;
8349 }
8350
8351 /* implement bltin and command here */
8352 if (cmdentry.cmdtype != CMDBUILTIN)
8353 break;
8354 if (spclbltin < 0)
8355 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8356 if (cmdentry.u.cmd == EXECCMD)
8357 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008358#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008359 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008360 path = oldpath;
8361 nargv = parse_command_args(argv, &path);
8362 if (!nargv)
8363 break;
8364 argc -= nargv - argv;
8365 argv = nargv;
8366 cmd_flag |= DO_NOFUNC;
8367 } else
8368#endif
8369 break;
8370 }
8371 }
8372
8373 if (status) {
8374 /* We have a redirection error. */
8375 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008376 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008377 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008378 exitstatus = status;
8379 goto out;
8380 }
8381
8382 /* Execute the command. */
8383 switch (cmdentry.cmdtype) {
8384 default:
8385 /* Fork off a child process if necessary. */
8386 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008387 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008388 jp = makejob(cmd, 1);
8389 if (forkshell(jp, cmd, FORK_FG) != 0) {
8390 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008391 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008392 break;
8393 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008394 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008395 }
8396 listsetvar(varlist.list, VEXPORT|VSTACK);
8397 shellexec(argv, path, cmdentry.u.index);
8398 /* NOTREACHED */
8399
8400 case CMDBUILTIN:
8401 cmdenviron = varlist.list;
8402 if (cmdenviron) {
8403 struct strlist *list = cmdenviron;
8404 int i = VNOSET;
8405 if (spclbltin > 0 || argc == 0) {
8406 i = 0;
8407 if (cmd_is_exec && argc > 1)
8408 i = VEXPORT;
8409 }
8410 listsetvar(list, i);
8411 }
8412 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8413 int exit_status;
8414 int i, j;
8415
8416 i = exception;
8417 if (i == EXEXIT)
8418 goto raise;
8419
8420 exit_status = 2;
8421 j = 0;
8422 if (i == EXINT)
8423 j = SIGINT;
8424 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008425 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008426 if (j)
8427 exit_status = j + 128;
8428 exitstatus = exit_status;
8429
8430 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008431 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008432 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008433 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008434 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008435 }
8436 break;
8437
8438 case CMDFUNCTION:
8439 listsetvar(varlist.list, 0);
8440 if (evalfun(cmdentry.u.func, argc, argv, flags))
8441 goto raise;
8442 break;
8443 }
8444
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008445 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008446 popredir(cmd_is_exec);
8447 if (lastarg)
8448 /* dsl: I think this is intended to be used to support
8449 * '_' in 'vi' command mode during line editing...
8450 * However I implemented that within libedit itself.
8451 */
8452 setvar("_", lastarg, 0);
8453 popstackmark(&smark);
8454}
8455
8456static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008457evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8458{
Eric Andersenc470f442003-07-28 09:56:35 +00008459 char *volatile savecmdname;
8460 struct jmploc *volatile savehandler;
8461 struct jmploc jmploc;
8462 int i;
8463
8464 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008465 i = setjmp(jmploc.loc);
8466 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008467 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008468 savehandler = exception_handler;
8469 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008470 commandname = argv[0];
8471 argptr = argv + 1;
8472 optptr = NULL; /* initialize nextopt */
8473 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008474 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008475 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008476 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008477 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008478 commandname = savecmdname;
8479 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008480 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008481
8482 return i;
8483}
8484
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008485static int
8486goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008487{
8488 return !*endofname(p);
8489}
8490
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008491
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008492/*
8493 * Search for a command. This is called before we fork so that the
8494 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008495 * the child. The check for "goodname" is an overly conservative
8496 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008497 */
Eric Andersenc470f442003-07-28 09:56:35 +00008498static void
8499prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008500{
8501 struct cmdentry entry;
8502
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008503 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8504 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008505}
8506
Eric Andersencb57d552001-06-28 07:25:16 +00008507
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008508/* ============ Builtin commands
8509 *
8510 * Builtin commands whose functions are closely tied to evaluation
8511 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008512 */
8513
8514/*
Eric Andersencb57d552001-06-28 07:25:16 +00008515 * Handle break and continue commands. Break, continue, and return are
8516 * all handled by setting the evalskip flag. The evaluation routines
8517 * above all check this flag, and if it is set they start skipping
8518 * commands rather than executing them. The variable skipcount is
8519 * the number of loops to break/continue, or the number of function
8520 * levels to return. (The latter is always 1.) It should probably
8521 * be an error to break out of more loops than exist, but it isn't
8522 * in the standard shell so we don't make it one here.
8523 */
Eric Andersenc470f442003-07-28 09:56:35 +00008524static int
8525breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008526{
8527 int n = argc > 1 ? number(argv[1]) : 1;
8528
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008529 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008530 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008531 if (n > loopnest)
8532 n = loopnest;
8533 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008534 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008535 skipcount = n;
8536 }
8537 return 0;
8538}
8539
Eric Andersenc470f442003-07-28 09:56:35 +00008540
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008541/* ============ input.c
8542 *
Eric Andersen90898442003-08-06 11:20:52 +00008543 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008544 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008545
Eric Andersenc470f442003-07-28 09:56:35 +00008546#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008547
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008548enum {
8549 INPUT_PUSH_FILE = 1,
8550 INPUT_NOFILE_OK = 2,
8551};
Eric Andersencb57d552001-06-28 07:25:16 +00008552
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008553/*
8554 * NEOF is returned by parsecmd when it encounters an end of file. It
8555 * must be distinct from NULL, so we use the address of a variable that
8556 * happens to be handy.
8557 */
8558static int plinno = 1; /* input line number */
8559/* number of characters left in input buffer */
8560static int parsenleft; /* copy of parsefile->nleft */
8561static int parselleft; /* copy of parsefile->lleft */
8562/* next character in input buffer */
8563static char *parsenextc; /* copy of parsefile->nextc */
8564
8565static int checkkwd;
8566/* values of checkkwd variable */
8567#define CHKALIAS 0x1
8568#define CHKKWD 0x2
8569#define CHKNL 0x4
8570
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008571static void
8572popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008573{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008574 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008575
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008576 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008577#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008578 if (sp->ap) {
8579 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8580 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008581 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008582 if (sp->string != sp->ap->val) {
8583 free(sp->string);
8584 }
8585 sp->ap->flag &= ~ALIASINUSE;
8586 if (sp->ap->flag & ALIASDEAD) {
8587 unalias(sp->ap->name);
8588 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008589 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008590#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008591 parsenextc = sp->prevstring;
8592 parsenleft = sp->prevnleft;
8593/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8594 parsefile->strpush = sp->prev;
8595 if (sp != &(parsefile->basestrpush))
8596 free(sp);
8597 INT_ON;
8598}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008599
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008600static int
8601preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008602{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008603 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008604 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008605 parsenextc = buf;
8606
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008607 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008608#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008609 if (!iflag || parsefile->fd)
8610 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8611 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008612#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008613 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008614#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008615 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8616 if (nr == 0) {
8617 /* Ctrl+C pressed */
8618 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008619 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008620 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008621 raise(SIGINT);
8622 return 1;
8623 }
Eric Andersenc470f442003-07-28 09:56:35 +00008624 goto retry;
8625 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008626 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008627 /* Ctrl+D presend */
8628 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008629 }
Eric Andersencb57d552001-06-28 07:25:16 +00008630 }
8631#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008632 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008633#endif
8634
8635 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008636 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
8637 int flags = fcntl(0, F_GETFL, 0);
8638 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008639 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008640 if (fcntl(0, F_SETFL, flags) >= 0) {
8641 out2str("sh: turning off NDELAY mode\n");
8642 goto retry;
8643 }
8644 }
8645 }
8646 }
8647 return nr;
8648}
8649
8650/*
8651 * Refill the input buffer and return the next input character:
8652 *
8653 * 1) If a string was pushed back on the input, pop it;
8654 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8655 * from a string so we can't refill the buffer, return EOF.
8656 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8657 * 4) Process input up to the next newline, deleting nul characters.
8658 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008659static int
Eric Andersenc470f442003-07-28 09:56:35 +00008660preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008661{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008662 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008663 int more;
8664 char savec;
8665
8666 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008667#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008668 if (parsenleft == -1 && parsefile->strpush->ap &&
8669 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008670 return PEOA;
8671 }
Eric Andersen2870d962001-07-02 17:27:21 +00008672#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008673 popstring();
8674 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008675 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008676 }
8677 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8678 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008679 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008680
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008681 more = parselleft;
8682 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008683 again:
8684 more = preadfd();
8685 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008686 parselleft = parsenleft = EOF_NLEFT;
8687 return PEOF;
8688 }
8689 }
8690
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008691 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008692
8693 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008694 for (;;) {
8695 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008696
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008697 more--;
8698 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008699
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008700 if (!c)
8701 memmove(q, q + 1, more);
8702 else {
8703 q++;
8704 if (c == '\n') {
8705 parsenleft = q - parsenextc - 1;
8706 break;
8707 }
Eric Andersencb57d552001-06-28 07:25:16 +00008708 }
8709
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008710 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008711 parsenleft = q - parsenextc - 1;
8712 if (parsenleft < 0)
8713 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008714 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008715 }
8716 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008717 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008718
8719 savec = *q;
8720 *q = '\0';
8721
8722 if (vflag) {
8723 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008724 }
8725
8726 *q = savec;
8727
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008728 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008729}
8730
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008731#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008732static int
8733pgetc(void)
8734{
8735 return pgetc_as_macro();
8736}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008737
8738#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8739#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008740#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008741#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008742#endif
8743
8744/*
8745 * Same as pgetc(), but ignores PEOA.
8746 */
8747#if ENABLE_ASH_ALIAS
8748static int
8749pgetc2(void)
8750{
8751 int c;
8752
8753 do {
8754 c = pgetc_macro();
8755 } while (c == PEOA);
8756 return c;
8757}
8758#else
8759static int
8760pgetc2(void)
8761{
8762 return pgetc_macro();
8763}
8764#endif
8765
8766/*
8767 * Read a line from the script.
8768 */
8769static char *
8770pfgets(char *line, int len)
8771{
8772 char *p = line;
8773 int nleft = len;
8774 int c;
8775
8776 while (--nleft > 0) {
8777 c = pgetc2();
8778 if (c == PEOF) {
8779 if (p == line)
8780 return NULL;
8781 break;
8782 }
8783 *p++ = c;
8784 if (c == '\n')
8785 break;
8786 }
8787 *p = '\0';
8788 return line;
8789}
8790
Eric Andersenc470f442003-07-28 09:56:35 +00008791/*
8792 * Undo the last call to pgetc. Only one character may be pushed back.
8793 * PEOF may be pushed back.
8794 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008795static void
Eric Andersenc470f442003-07-28 09:56:35 +00008796pungetc(void)
8797{
8798 parsenleft++;
8799 parsenextc--;
8800}
Eric Andersencb57d552001-06-28 07:25:16 +00008801
8802/*
8803 * Push a string back onto the input at this current parsefile level.
8804 * We handle aliases this way.
8805 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008806static void
Eric Andersenc470f442003-07-28 09:56:35 +00008807pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008808{
Eric Andersencb57d552001-06-28 07:25:16 +00008809 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008810 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008811
Eric Andersenc470f442003-07-28 09:56:35 +00008812 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008813 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008814/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8815 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008816 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008817 sp->prev = parsefile->strpush;
8818 parsefile->strpush = sp;
8819 } else
8820 sp = parsefile->strpush = &(parsefile->basestrpush);
8821 sp->prevstring = parsenextc;
8822 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008823#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008824 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008825 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008826 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008827 sp->string = s;
8828 }
Eric Andersen2870d962001-07-02 17:27:21 +00008829#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008830 parsenextc = s;
8831 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008832 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008833}
8834
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008835/*
8836 * To handle the "." command, a stack of input files is used. Pushfile
8837 * adds a new entry to the stack and popfile restores the previous level.
8838 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008839static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008840pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008841{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008842 struct parsefile *pf;
8843
8844 parsefile->nleft = parsenleft;
8845 parsefile->lleft = parselleft;
8846 parsefile->nextc = parsenextc;
8847 parsefile->linno = plinno;
8848 pf = ckmalloc(sizeof(*pf));
8849 pf->prev = parsefile;
8850 pf->fd = -1;
8851 pf->strpush = NULL;
8852 pf->basestrpush.prev = NULL;
8853 parsefile = pf;
8854}
8855
8856static void
8857popfile(void)
8858{
8859 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008860
Denis Vlasenkob012b102007-02-19 22:43:01 +00008861 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008862 if (pf->fd >= 0)
8863 close(pf->fd);
8864 if (pf->buf)
8865 free(pf->buf);
8866 while (pf->strpush)
8867 popstring();
8868 parsefile = pf->prev;
8869 free(pf);
8870 parsenleft = parsefile->nleft;
8871 parselleft = parsefile->lleft;
8872 parsenextc = parsefile->nextc;
8873 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008874 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008875}
8876
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008877/*
8878 * Return to top level.
8879 */
8880static void
8881popallfiles(void)
8882{
8883 while (parsefile != &basepf)
8884 popfile();
8885}
8886
8887/*
8888 * Close the file(s) that the shell is reading commands from. Called
8889 * after a fork is done.
8890 */
8891static void
8892closescript(void)
8893{
8894 popallfiles();
8895 if (parsefile->fd > 0) {
8896 close(parsefile->fd);
8897 parsefile->fd = 0;
8898 }
8899}
8900
8901/*
8902 * Like setinputfile, but takes an open file descriptor. Call this with
8903 * interrupts off.
8904 */
8905static void
8906setinputfd(int fd, int push)
8907{
8908 fcntl(fd, F_SETFD, FD_CLOEXEC);
8909 if (push) {
8910 pushfile();
8911 parsefile->buf = 0;
8912 }
8913 parsefile->fd = fd;
8914 if (parsefile->buf == NULL)
8915 parsefile->buf = ckmalloc(IBUFSIZ);
8916 parselleft = parsenleft = 0;
8917 plinno = 1;
8918}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008919
Eric Andersenc470f442003-07-28 09:56:35 +00008920/*
8921 * Set the input to take input from a file. If push is set, push the
8922 * old input onto the stack first.
8923 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008924static int
8925setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008926{
8927 int fd;
8928 int fd2;
8929
Denis Vlasenkob012b102007-02-19 22:43:01 +00008930 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008931 fd = open(fname, O_RDONLY);
8932 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008933 if (flags & INPUT_NOFILE_OK)
8934 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008935 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008936 }
Eric Andersenc470f442003-07-28 09:56:35 +00008937 if (fd < 10) {
8938 fd2 = copyfd(fd, 10);
8939 close(fd);
8940 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008941 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008942 fd = fd2;
8943 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008944 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008945 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008946 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008947 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008948}
8949
Eric Andersencb57d552001-06-28 07:25:16 +00008950/*
8951 * Like setinputfile, but takes input from a string.
8952 */
Eric Andersenc470f442003-07-28 09:56:35 +00008953static void
8954setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008955{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008956 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008957 pushfile();
8958 parsenextc = string;
8959 parsenleft = strlen(string);
8960 parsefile->buf = NULL;
8961 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008962 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008963}
8964
8965
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008966/* ============ mail.c
8967 *
8968 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008969 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008970
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008971#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008972
Eric Andersencb57d552001-06-28 07:25:16 +00008973#define MAXMBOXES 10
8974
Eric Andersenc470f442003-07-28 09:56:35 +00008975/* times of mailboxes */
8976static time_t mailtime[MAXMBOXES];
8977/* Set if MAIL or MAILPATH is changed. */
8978static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008979
Eric Andersencb57d552001-06-28 07:25:16 +00008980/*
Eric Andersenc470f442003-07-28 09:56:35 +00008981 * Print appropriate message(s) if mail has arrived.
8982 * If mail_var_path_changed is set,
8983 * then the value of MAIL has mail_var_path_changed,
8984 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008985 */
Eric Andersenc470f442003-07-28 09:56:35 +00008986static void
8987chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008988{
Eric Andersencb57d552001-06-28 07:25:16 +00008989 const char *mpath;
8990 char *p;
8991 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008992 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008993 struct stackmark smark;
8994 struct stat statb;
8995
Eric Andersencb57d552001-06-28 07:25:16 +00008996 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008997 mpath = mpathset() ? mpathval() : mailval();
8998 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008999 p = padvance(&mpath, nullstr);
9000 if (p == NULL)
9001 break;
9002 if (*p == '\0')
9003 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009004 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009005#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009006 if (q[-1] != '/')
9007 abort();
9008#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009009 q[-1] = '\0'; /* delete trailing '/' */
9010 if (stat(p, &statb) < 0) {
9011 *mtp = 0;
9012 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009013 }
Eric Andersenc470f442003-07-28 09:56:35 +00009014 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9015 fprintf(
9016 stderr, snlfmt,
9017 pathopt ? pathopt : "you have mail"
9018 );
9019 }
9020 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009021 }
Eric Andersenc470f442003-07-28 09:56:35 +00009022 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009023 popstackmark(&smark);
9024}
Eric Andersencb57d552001-06-28 07:25:16 +00009025
Eric Andersenc470f442003-07-28 09:56:35 +00009026static void
9027changemail(const char *val)
9028{
9029 mail_var_path_changed++;
9030}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009031
Denis Vlasenko131ae172007-02-18 13:00:19 +00009032#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009033
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009034
9035/* ============ ??? */
9036
Eric Andersencb57d552001-06-28 07:25:16 +00009037/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009038 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009039 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009040static void
9041setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009042{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009043 char **newparam;
9044 char **ap;
9045 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009046
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009047 for (nparam = 0; argv[nparam]; nparam++);
9048 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9049 while (*argv) {
9050 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009051 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009052 *ap = NULL;
9053 freeparam(&shellparam);
9054 shellparam.malloc = 1;
9055 shellparam.nparam = nparam;
9056 shellparam.p = newparam;
9057#if ENABLE_ASH_GETOPTS
9058 shellparam.optind = 1;
9059 shellparam.optoff = -1;
9060#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009061}
9062
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009063/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009064 * Process shell options. The global variable argptr contains a pointer
9065 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009066 */
9067static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009068minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009069{
9070 int i;
9071
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009072 if (name) {
9073 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009074 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009075 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009076 return;
9077 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009078 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009079 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009080 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009081 out1str("Current option settings\n");
9082 for (i = 0; i < NOPTS; i++)
9083 out1fmt("%-16s%s\n", optnames(i),
9084 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009085}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009086static void
9087setoption(int flag, int val)
9088{
9089 int i;
9090
9091 for (i = 0; i < NOPTS; i++) {
9092 if (optletters(i) == flag) {
9093 optlist[i] = val;
9094 return;
9095 }
9096 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009097 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009098 /* NOTREACHED */
9099}
Eric Andersenc470f442003-07-28 09:56:35 +00009100static void
9101options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009102{
9103 char *p;
9104 int val;
9105 int c;
9106
9107 if (cmdline)
9108 minusc = NULL;
9109 while ((p = *argptr) != NULL) {
9110 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009111 c = *p++;
9112 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009113 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009114 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009115 if (!cmdline) {
9116 /* "-" means turn off -x and -v */
9117 if (p[0] == '\0')
9118 xflag = vflag = 0;
9119 /* "--" means reset params */
9120 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009121 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009122 }
Eric Andersenc470f442003-07-28 09:56:35 +00009123 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009124 }
9125 } else if (c == '+') {
9126 val = 0;
9127 } else {
9128 argptr--;
9129 break;
9130 }
9131 while ((c = *p++) != '\0') {
9132 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00009133 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00009134 } else if (c == 'o') {
9135 minus_o(*argptr, val);
9136 if (*argptr)
9137 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00009138 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009139 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009140 isloginsh = 1;
9141 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009142 } else {
9143 setoption(c, val);
9144 }
9145 }
9146 }
9147}
9148
Eric Andersencb57d552001-06-28 07:25:16 +00009149/*
Eric Andersencb57d552001-06-28 07:25:16 +00009150 * The shift builtin command.
9151 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009152static int
Eric Andersenc470f442003-07-28 09:56:35 +00009153shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009154{
9155 int n;
9156 char **ap1, **ap2;
9157
9158 n = 1;
9159 if (argc > 1)
9160 n = number(argv[1]);
9161 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009162 ash_msg_and_raise_error("can't shift that many");
9163 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009164 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009165 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009166 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009167 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009168 }
9169 ap2 = shellparam.p;
9170 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009171#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009172 shellparam.optind = 1;
9173 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009174#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009175 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009176 return 0;
9177}
9178
Eric Andersencb57d552001-06-28 07:25:16 +00009179/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009180 * POSIX requires that 'set' (but not export or readonly) output the
9181 * variables in lexicographic order - by the locale's collating order (sigh).
9182 * Maybe we could keep them in an ordered balanced binary tree
9183 * instead of hashed lists.
9184 * For now just roll 'em through qsort for printing...
9185 */
9186static int
9187showvars(const char *sep_prefix, int on, int off)
9188{
9189 const char *sep;
9190 char **ep, **epend;
9191
9192 ep = listvars(on, off, &epend);
9193 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9194
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009195 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009196
9197 for (; ep < epend; ep++) {
9198 const char *p;
9199 const char *q;
9200
9201 p = strchrnul(*ep, '=');
9202 q = nullstr;
9203 if (*p)
9204 q = single_quote(++p);
9205 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9206 }
9207 return 0;
9208}
9209
9210/*
Eric Andersencb57d552001-06-28 07:25:16 +00009211 * The set command builtin.
9212 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009213static int
Eric Andersenc470f442003-07-28 09:56:35 +00009214setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009215{
9216 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009217 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009218 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009219 options(0);
9220 optschanged();
9221 if (*argptr != NULL) {
9222 setparam(argptr);
9223 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009224 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009225 return 0;
9226}
9227
Denis Vlasenko131ae172007-02-18 13:00:19 +00009228#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009229/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009230static void
9231change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009232{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009233 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009234 /* "get", generate */
9235 char buf[16];
9236
9237 rseed = rseed * 1103515245 + 12345;
9238 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9239 /* set without recursion */
9240 setvar(vrandom.text, buf, VNOFUNC);
9241 vrandom.flags &= ~VNOFUNC;
9242 } else {
9243 /* set/reset */
9244 rseed = strtoul(value, (char **)NULL, 10);
9245 }
Eric Andersenef02f822004-03-11 13:34:24 +00009246}
Eric Andersen16767e22004-03-16 05:14:10 +00009247#endif
9248
Denis Vlasenko131ae172007-02-18 13:00:19 +00009249#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009250static int
Eric Andersenc470f442003-07-28 09:56:35 +00009251getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009252{
9253 char *p, *q;
9254 char c = '?';
9255 int done = 0;
9256 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009257 char s[12];
9258 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009259
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009260 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009261 return 1;
9262 optnext = optfirst + *param_optind - 1;
9263
9264 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009265 p = NULL;
9266 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009267 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009268 if (p == NULL || *p == '\0') {
9269 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009270 p = *optnext;
9271 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009272 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009273 p = NULL;
9274 done = 1;
9275 goto out;
9276 }
9277 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009278 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009279 goto atend;
9280 }
9281
9282 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009283 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009284 if (*q == '\0') {
9285 if (optstr[0] == ':') {
9286 s[0] = c;
9287 s[1] = '\0';
9288 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009289 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009290 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009291 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009292 }
9293 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009294 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009295 }
9296 if (*++q == ':')
9297 q++;
9298 }
9299
9300 if (*++q == ':') {
9301 if (*p == '\0' && (p = *optnext) == NULL) {
9302 if (optstr[0] == ':') {
9303 s[0] = c;
9304 s[1] = '\0';
9305 err |= setvarsafe("OPTARG", s, 0);
9306 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009307 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009308 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009309 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009310 c = '?';
9311 }
Eric Andersenc470f442003-07-28 09:56:35 +00009312 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009313 }
9314
9315 if (p == *optnext)
9316 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009317 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009318 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009319 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009320 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009321 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009322 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009323 *param_optind = optnext - optfirst + 1;
9324 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009325 err |= setvarsafe("OPTIND", s, VNOFUNC);
9326 s[0] = c;
9327 s[1] = '\0';
9328 err |= setvarsafe(optvar, s, 0);
9329 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009330 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009331 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009332 flush_stdout_stderr();
9333 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009334 }
9335 return done;
9336}
Eric Andersenc470f442003-07-28 09:56:35 +00009337
9338/*
9339 * The getopts builtin. Shellparam.optnext points to the next argument
9340 * to be processed. Shellparam.optptr points to the next character to
9341 * be processed in the current argument. If shellparam.optnext is NULL,
9342 * then it's the first time getopts has been called.
9343 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009344static int
Eric Andersenc470f442003-07-28 09:56:35 +00009345getoptscmd(int argc, char **argv)
9346{
9347 char **optbase;
9348
9349 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009350 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009351 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009352 optbase = shellparam.p;
9353 if (shellparam.optind > shellparam.nparam + 1) {
9354 shellparam.optind = 1;
9355 shellparam.optoff = -1;
9356 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009357 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009358 optbase = &argv[3];
9359 if (shellparam.optind > argc - 2) {
9360 shellparam.optind = 1;
9361 shellparam.optoff = -1;
9362 }
9363 }
9364
9365 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009366 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009367}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009368#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009369
Eric Andersencb57d552001-06-28 07:25:16 +00009370
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009371/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009372
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009373static int tokpushback; /* last token pushed back */
9374#define NEOF ((union node *)&tokpushback)
9375static int parsebackquote; /* nonzero if we are inside backquotes */
9376static int lasttoken; /* last token read */
9377static char *wordtext; /* text of last word returned by readtoken */
9378static struct nodelist *backquotelist;
9379static union node *redirnode;
9380static struct heredoc *heredoc;
9381static int quoteflag; /* set if (part of) last token was quoted */
9382
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009383static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9384static void
9385raise_error_syntax(const char *msg)
9386{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009387 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009388 /* NOTREACHED */
9389}
9390
9391/*
9392 * Called when an unexpected token is read during the parse. The argument
9393 * is the token that is expected, or -1 if more than one type of token can
9394 * occur at this point.
9395 */
9396static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9397static void
9398raise_error_unexpected_syntax(int token)
9399{
9400 char msg[64];
9401 int l;
9402
9403 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9404 if (token >= 0)
9405 sprintf(msg + l, " (expecting %s)", tokname(token));
9406 raise_error_syntax(msg);
9407 /* NOTREACHED */
9408}
Eric Andersencb57d552001-06-28 07:25:16 +00009409
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009410#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009411
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009412struct heredoc {
9413 struct heredoc *next; /* next here document in list */
9414 union node *here; /* redirection node */
9415 char *eofmark; /* string indicating end of input */
9416 int striptabs; /* if set, strip leading tabs */
9417};
Eric Andersencb57d552001-06-28 07:25:16 +00009418
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009419static struct heredoc *heredoclist; /* list of here documents to read */
9420
9421/* parsing is heavily cross-recursive, need these forward decls */
9422static union node *andor(void);
9423static union node *pipeline(void);
9424static union node *parse_command(void);
9425static void parseheredoc(void);
9426static char peektoken(void);
9427static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009428
Eric Andersenc470f442003-07-28 09:56:35 +00009429static union node *
9430list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009431{
9432 union node *n1, *n2, *n3;
9433 int tok;
9434
Eric Andersenc470f442003-07-28 09:56:35 +00009435 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9436 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009437 return NULL;
9438 n1 = NULL;
9439 for (;;) {
9440 n2 = andor();
9441 tok = readtoken();
9442 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009443 if (n2->type == NPIPE) {
9444 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009445 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009446 if (n2->type != NREDIR) {
9447 n3 = stalloc(sizeof(struct nredir));
9448 n3->nredir.n = n2;
9449 n3->nredir.redirect = NULL;
9450 n2 = n3;
9451 }
9452 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009453 }
9454 }
9455 if (n1 == NULL) {
9456 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009457 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009458 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009459 n3->type = NSEMI;
9460 n3->nbinary.ch1 = n1;
9461 n3->nbinary.ch2 = n2;
9462 n1 = n3;
9463 }
9464 switch (tok) {
9465 case TBACKGND:
9466 case TSEMI:
9467 tok = readtoken();
9468 /* fall through */
9469 case TNL:
9470 if (tok == TNL) {
9471 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009472 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009473 return n1;
9474 } else {
9475 tokpushback++;
9476 }
Eric Andersenc470f442003-07-28 09:56:35 +00009477 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009478 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009479 return n1;
9480 break;
9481 case TEOF:
9482 if (heredoclist)
9483 parseheredoc();
9484 else
Eric Andersenc470f442003-07-28 09:56:35 +00009485 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009486 return n1;
9487 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009488 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009489 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009490 tokpushback++;
9491 return n1;
9492 }
9493 }
9494}
9495
Eric Andersenc470f442003-07-28 09:56:35 +00009496static union node *
9497andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009498{
Eric Andersencb57d552001-06-28 07:25:16 +00009499 union node *n1, *n2, *n3;
9500 int t;
9501
Eric Andersencb57d552001-06-28 07:25:16 +00009502 n1 = pipeline();
9503 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009504 t = readtoken();
9505 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009506 t = NAND;
9507 } else if (t == TOR) {
9508 t = NOR;
9509 } else {
9510 tokpushback++;
9511 return n1;
9512 }
Eric Andersenc470f442003-07-28 09:56:35 +00009513 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009514 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009515 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009516 n3->type = t;
9517 n3->nbinary.ch1 = n1;
9518 n3->nbinary.ch2 = n2;
9519 n1 = n3;
9520 }
9521}
9522
Eric Andersenc470f442003-07-28 09:56:35 +00009523static union node *
9524pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009525{
Eric Andersencb57d552001-06-28 07:25:16 +00009526 union node *n1, *n2, *pipenode;
9527 struct nodelist *lp, *prev;
9528 int negate;
9529
9530 negate = 0;
9531 TRACE(("pipeline: entered\n"));
9532 if (readtoken() == TNOT) {
9533 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009534 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009535 } else
9536 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009537 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009538 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009539 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009540 pipenode->type = NPIPE;
9541 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009542 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009543 pipenode->npipe.cmdlist = lp;
9544 lp->n = n1;
9545 do {
9546 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009547 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009548 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009549 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009550 prev->next = lp;
9551 } while (readtoken() == TPIPE);
9552 lp->next = NULL;
9553 n1 = pipenode;
9554 }
9555 tokpushback++;
9556 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009557 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009558 n2->type = NNOT;
9559 n2->nnot.com = n1;
9560 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009561 }
9562 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009563}
9564
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009565static union node *
9566makename(void)
9567{
9568 union node *n;
9569
9570 n = stalloc(sizeof(struct narg));
9571 n->type = NARG;
9572 n->narg.next = NULL;
9573 n->narg.text = wordtext;
9574 n->narg.backquote = backquotelist;
9575 return n;
9576}
9577
9578static void
9579fixredir(union node *n, const char *text, int err)
9580{
9581 TRACE(("Fix redir %s %d\n", text, err));
9582 if (!err)
9583 n->ndup.vname = NULL;
9584
9585 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009586 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009587 else if (LONE_DASH(text))
9588 n->ndup.dupfd = -1;
9589 else {
9590 if (err)
9591 raise_error_syntax("Bad fd number");
9592 n->ndup.vname = makename();
9593 }
9594}
9595
9596/*
9597 * Returns true if the text contains nothing to expand (no dollar signs
9598 * or backquotes).
9599 */
9600static int
9601noexpand(char *text)
9602{
9603 char *p;
9604 char c;
9605
9606 p = text;
9607 while ((c = *p++) != '\0') {
9608 if (c == CTLQUOTEMARK)
9609 continue;
9610 if (c == CTLESC)
9611 p++;
9612 else if (SIT(c, BASESYNTAX) == CCTL)
9613 return 0;
9614 }
9615 return 1;
9616}
9617
9618static void
9619parsefname(void)
9620{
9621 union node *n = redirnode;
9622
9623 if (readtoken() != TWORD)
9624 raise_error_unexpected_syntax(-1);
9625 if (n->type == NHERE) {
9626 struct heredoc *here = heredoc;
9627 struct heredoc *p;
9628 int i;
9629
9630 if (quoteflag == 0)
9631 n->type = NXHERE;
9632 TRACE(("Here document %d\n", n->type));
9633 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9634 raise_error_syntax("Illegal eof marker for << redirection");
9635 rmescapes(wordtext);
9636 here->eofmark = wordtext;
9637 here->next = NULL;
9638 if (heredoclist == NULL)
9639 heredoclist = here;
9640 else {
9641 for (p = heredoclist; p->next; p = p->next);
9642 p->next = here;
9643 }
9644 } else if (n->type == NTOFD || n->type == NFROMFD) {
9645 fixredir(n, wordtext, 0);
9646 } else {
9647 n->nfile.fname = makename();
9648 }
9649}
Eric Andersencb57d552001-06-28 07:25:16 +00009650
Eric Andersenc470f442003-07-28 09:56:35 +00009651static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009652simplecmd(void)
9653{
9654 union node *args, **app;
9655 union node *n = NULL;
9656 union node *vars, **vpp;
9657 union node **rpp, *redir;
9658 int savecheckkwd;
9659
9660 args = NULL;
9661 app = &args;
9662 vars = NULL;
9663 vpp = &vars;
9664 redir = NULL;
9665 rpp = &redir;
9666
9667 savecheckkwd = CHKALIAS;
9668 for (;;) {
9669 checkkwd = savecheckkwd;
9670 switch (readtoken()) {
9671 case TWORD:
9672 n = stalloc(sizeof(struct narg));
9673 n->type = NARG;
9674 n->narg.text = wordtext;
9675 n->narg.backquote = backquotelist;
9676 if (savecheckkwd && isassignment(wordtext)) {
9677 *vpp = n;
9678 vpp = &n->narg.next;
9679 } else {
9680 *app = n;
9681 app = &n->narg.next;
9682 savecheckkwd = 0;
9683 }
9684 break;
9685 case TREDIR:
9686 *rpp = n = redirnode;
9687 rpp = &n->nfile.next;
9688 parsefname(); /* read name of redirection file */
9689 break;
9690 case TLP:
9691 if (args && app == &args->narg.next
9692 && !vars && !redir
9693 ) {
9694 struct builtincmd *bcmd;
9695 const char *name;
9696
9697 /* We have a function */
9698 if (readtoken() != TRP)
9699 raise_error_unexpected_syntax(TRP);
9700 name = n->narg.text;
9701 if (!goodname(name)
9702 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9703 ) {
9704 raise_error_syntax("Bad function name");
9705 }
9706 n->type = NDEFUN;
9707 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9708 n->narg.next = parse_command();
9709 return n;
9710 }
9711 /* fall through */
9712 default:
9713 tokpushback++;
9714 goto out;
9715 }
9716 }
9717 out:
9718 *app = NULL;
9719 *vpp = NULL;
9720 *rpp = NULL;
9721 n = stalloc(sizeof(struct ncmd));
9722 n->type = NCMD;
9723 n->ncmd.args = args;
9724 n->ncmd.assign = vars;
9725 n->ncmd.redirect = redir;
9726 return n;
9727}
9728
9729static union node *
9730parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009731{
Eric Andersencb57d552001-06-28 07:25:16 +00009732 union node *n1, *n2;
9733 union node *ap, **app;
9734 union node *cp, **cpp;
9735 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009736 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009737 int t;
9738
9739 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009740 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009741
Eric Andersencb57d552001-06-28 07:25:16 +00009742 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009743 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009744 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009745 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009746 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009747 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009748 n1->type = NIF;
9749 n1->nif.test = list(0);
9750 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009751 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009752 n1->nif.ifpart = list(0);
9753 n2 = n1;
9754 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009755 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009756 n2 = n2->nif.elsepart;
9757 n2->type = NIF;
9758 n2->nif.test = list(0);
9759 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009760 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009761 n2->nif.ifpart = list(0);
9762 }
9763 if (lasttoken == TELSE)
9764 n2->nif.elsepart = list(0);
9765 else {
9766 n2->nif.elsepart = NULL;
9767 tokpushback++;
9768 }
Eric Andersenc470f442003-07-28 09:56:35 +00009769 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009770 break;
9771 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009772 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009773 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009774 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009775 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009776 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009777 got = readtoken();
9778 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009779 TRACE(("expecting DO got %s %s\n", tokname(got),
9780 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009781 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009782 }
9783 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009784 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009785 break;
9786 }
9787 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009788 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009789 raise_error_syntax("Bad for loop variable");
9790 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009791 n1->type = NFOR;
9792 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009793 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009794 if (readtoken() == TIN) {
9795 app = &ap;
9796 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009797 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009798 n2->type = NARG;
9799 n2->narg.text = wordtext;
9800 n2->narg.backquote = backquotelist;
9801 *app = n2;
9802 app = &n2->narg.next;
9803 }
9804 *app = NULL;
9805 n1->nfor.args = ap;
9806 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009807 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009808 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009809 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009810 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009811 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009812 n2->narg.backquote = NULL;
9813 n2->narg.next = NULL;
9814 n1->nfor.args = n2;
9815 /*
9816 * Newline or semicolon here is optional (but note
9817 * that the original Bourne shell only allowed NL).
9818 */
9819 if (lasttoken != TNL && lasttoken != TSEMI)
9820 tokpushback++;
9821 }
Eric Andersenc470f442003-07-28 09:56:35 +00009822 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009823 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009824 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009825 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009826 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009827 break;
9828 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009829 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009830 n1->type = NCASE;
9831 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009832 raise_error_unexpected_syntax(TWORD);
9833 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009834 n2->type = NARG;
9835 n2->narg.text = wordtext;
9836 n2->narg.backquote = backquotelist;
9837 n2->narg.next = NULL;
9838 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009839 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009840 } while (readtoken() == TNL);
9841 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009842 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009843 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009844 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009845 checkkwd = CHKNL | CHKKWD;
9846 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009847 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009848 if (lasttoken == TLP)
9849 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009850 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009851 cp->type = NCLIST;
9852 app = &cp->nclist.pattern;
9853 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009854 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009855 ap->type = NARG;
9856 ap->narg.text = wordtext;
9857 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009858 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009859 break;
9860 app = &ap->narg.next;
9861 readtoken();
9862 }
9863 ap->narg.next = NULL;
9864 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009865 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009866 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009867
Eric Andersenc470f442003-07-28 09:56:35 +00009868 cpp = &cp->nclist.next;
9869
9870 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009871 t = readtoken();
9872 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009873 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009874 raise_error_unexpected_syntax(TENDCASE);
9875 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009876 }
Eric Andersenc470f442003-07-28 09:56:35 +00009877 }
Eric Andersencb57d552001-06-28 07:25:16 +00009878 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009879 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009880 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009881 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009882 n1->type = NSUBSHELL;
9883 n1->nredir.n = list(0);
9884 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009885 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009886 break;
9887 case TBEGIN:
9888 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009889 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009890 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009891 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009892 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009893 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009894 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009895 }
9896
Eric Andersenc470f442003-07-28 09:56:35 +00009897 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009898 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009899
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009900 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009901 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009902 checkkwd = CHKKWD | CHKALIAS;
9903 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009904 while (readtoken() == TREDIR) {
9905 *rpp = n2 = redirnode;
9906 rpp = &n2->nfile.next;
9907 parsefname();
9908 }
9909 tokpushback++;
9910 *rpp = NULL;
9911 if (redir) {
9912 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009913 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009914 n2->type = NREDIR;
9915 n2->nredir.n = n1;
9916 n1 = n2;
9917 }
9918 n1->nredir.redirect = redir;
9919 }
Eric Andersencb57d552001-06-28 07:25:16 +00009920 return n1;
9921}
9922
Eric Andersencb57d552001-06-28 07:25:16 +00009923/*
9924 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9925 * is not NULL, read a here document. In the latter case, eofmark is the
9926 * word which marks the end of the document and striptabs is true if
9927 * leading tabs should be stripped from the document. The argument firstc
9928 * is the first character of the input token or document.
9929 *
9930 * Because C does not have internal subroutines, I have simulated them
9931 * using goto's to implement the subroutine linkage. The following macros
9932 * will run code that appears at the end of readtoken1.
9933 */
9934
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009935static int parsebackquote; /* nonzero if we are inside backquotes */
9936
Eric Andersen2870d962001-07-02 17:27:21 +00009937#define CHECKEND() {goto checkend; checkend_return:;}
9938#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9939#define PARSESUB() {goto parsesub; parsesub_return:;}
9940#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9941#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9942#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009943
9944static int
Eric Andersenc470f442003-07-28 09:56:35 +00009945readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009946{
Eric Andersencb57d552001-06-28 07:25:16 +00009947 int c = firstc;
9948 char *out;
9949 int len;
9950 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009951 struct nodelist *bqlist = 0;
9952 int quotef = 0;
9953 int dblquote = 0;
9954 int varnest = 0; /* levels of variables expansion */
9955 int arinest = 0; /* levels of arithmetic expansion */
9956 int parenlevel = 0; /* levels of parens in arithmetic */
9957 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9958 int oldstyle = 0;
9959 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009960#if __GNUC__
9961 /* Avoid longjmp clobbering */
9962 (void) &out;
9963 (void) &quotef;
9964 (void) &dblquote;
9965 (void) &varnest;
9966 (void) &arinest;
9967 (void) &parenlevel;
9968 (void) &dqvarnest;
9969 (void) &oldstyle;
9970 (void) &prevsyntax;
9971 (void) &syntax;
9972#endif
9973
9974 startlinno = plinno;
9975 dblquote = 0;
9976 if (syntax == DQSYNTAX)
9977 dblquote = 1;
9978 quotef = 0;
9979 bqlist = NULL;
9980 varnest = 0;
9981 arinest = 0;
9982 parenlevel = 0;
9983 dqvarnest = 0;
9984
9985 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009986 loop: { /* for each line, until end of word */
9987 CHECKEND(); /* set c to PEOF if at end of here document */
9988 for (;;) { /* until end of line or end of word */
9989 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009990 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009991 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009992 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009993 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009994 USTPUTC(c, out);
9995 plinno++;
9996 if (doprompt)
9997 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009998 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009999 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010000 case CWORD:
10001 USTPUTC(c, out);
10002 break;
10003 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010004 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010005 USTPUTC(CTLESC, out);
10006 USTPUTC(c, out);
10007 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010008 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010009 c = pgetc2();
10010 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010011 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010012 USTPUTC('\\', out);
10013 pungetc();
10014 } else if (c == '\n') {
10015 if (doprompt)
10016 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010017 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010018 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010019 c != '\\' && c != '`' &&
10020 c != '$' && (
10021 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010022 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010023 ) {
10024 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010025 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010026 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010027 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010028 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010029 USTPUTC(c, out);
10030 quotef++;
10031 }
10032 break;
10033 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010034 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010035 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010036 if (eofmark == NULL) {
10037 USTPUTC(CTLQUOTEMARK, out);
10038 }
Eric Andersencb57d552001-06-28 07:25:16 +000010039 break;
10040 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010041 syntax = DQSYNTAX;
10042 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010043 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010044 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010045 if (eofmark != NULL && arinest == 0
10046 && varnest == 0
10047 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010048 USTPUTC(c, out);
10049 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010050 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010051 syntax = BASESYNTAX;
10052 dblquote = 0;
10053 }
10054 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +000010055 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010056 }
10057 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010058 case CVAR: /* '$' */
10059 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010060 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010061 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010062 if (varnest > 0) {
10063 varnest--;
10064 if (dqvarnest > 0) {
10065 dqvarnest--;
10066 }
10067 USTPUTC(CTLENDVAR, out);
10068 } else {
10069 USTPUTC(c, out);
10070 }
10071 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010072#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010073 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010074 parenlevel++;
10075 USTPUTC(c, out);
10076 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010077 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010078 if (parenlevel > 0) {
10079 USTPUTC(c, out);
10080 --parenlevel;
10081 } else {
10082 if (pgetc() == ')') {
10083 if (--arinest == 0) {
10084 USTPUTC(CTLENDARI, out);
10085 syntax = prevsyntax;
10086 if (syntax == DQSYNTAX)
10087 dblquote = 1;
10088 else
10089 dblquote = 0;
10090 } else
10091 USTPUTC(')', out);
10092 } else {
10093 /*
10094 * unbalanced parens
10095 * (don't 2nd guess - no error)
10096 */
10097 pungetc();
10098 USTPUTC(')', out);
10099 }
10100 }
10101 break;
10102#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010103 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010104 PARSEBACKQOLD();
10105 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010106 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010107 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010108 case CIGN:
10109 break;
10110 default:
10111 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010112 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010113#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010114 if (c != PEOA)
10115#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010116 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010117
Eric Andersencb57d552001-06-28 07:25:16 +000010118 }
10119 c = pgetc_macro();
10120 }
10121 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010122 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010123#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010124 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010125 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010126#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010127 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010128 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010129 if (varnest != 0) {
10130 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010131 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010132 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010133 }
10134 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010135 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010136 out = stackblock();
10137 if (eofmark == NULL) {
10138 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010139 && quotef == 0
10140 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010141 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010142 PARSEREDIR();
10143 return lasttoken = TREDIR;
10144 } else {
10145 pungetc();
10146 }
10147 }
10148 quoteflag = quotef;
10149 backquotelist = bqlist;
10150 grabstackblock(len);
10151 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010152 lasttoken = TWORD;
10153 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010154/* end of readtoken routine */
10155
Eric Andersencb57d552001-06-28 07:25:16 +000010156/*
10157 * Check to see whether we are at the end of the here document. When this
10158 * is called, c is set to the first character of the next input line. If
10159 * we are at the end of the here document, this routine sets the c to PEOF.
10160 */
Eric Andersenc470f442003-07-28 09:56:35 +000010161checkend: {
10162 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010163#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010164 if (c == PEOA) {
10165 c = pgetc2();
10166 }
10167#endif
10168 if (striptabs) {
10169 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010170 c = pgetc2();
10171 }
Eric Andersenc470f442003-07-28 09:56:35 +000010172 }
10173 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010174 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010175 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010176
Eric Andersenc470f442003-07-28 09:56:35 +000010177 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010178 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010179 if (*p == '\n' && *q == '\0') {
10180 c = PEOF;
10181 plinno++;
10182 needprompt = doprompt;
10183 } else {
10184 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010185 }
10186 }
10187 }
10188 }
Eric Andersenc470f442003-07-28 09:56:35 +000010189 goto checkend_return;
10190}
Eric Andersencb57d552001-06-28 07:25:16 +000010191
Eric Andersencb57d552001-06-28 07:25:16 +000010192/*
10193 * Parse a redirection operator. The variable "out" points to a string
10194 * specifying the fd to be redirected. The variable "c" contains the
10195 * first character of the redirection operator.
10196 */
Eric Andersenc470f442003-07-28 09:56:35 +000010197parseredir: {
10198 char fd = *out;
10199 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010200
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010201 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010202 if (c == '>') {
10203 np->nfile.fd = 1;
10204 c = pgetc();
10205 if (c == '>')
10206 np->type = NAPPEND;
10207 else if (c == '|')
10208 np->type = NCLOBBER;
10209 else if (c == '&')
10210 np->type = NTOFD;
10211 else {
10212 np->type = NTO;
10213 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010214 }
Eric Andersenc470f442003-07-28 09:56:35 +000010215 } else { /* c == '<' */
10216 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010217 c = pgetc();
10218 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010219 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010220 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010221 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010222 np->nfile.fd = 0;
10223 }
10224 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010225 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010226 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010227 c = pgetc();
10228 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010229 heredoc->striptabs = 1;
10230 } else {
10231 heredoc->striptabs = 0;
10232 pungetc();
10233 }
10234 break;
10235
10236 case '&':
10237 np->type = NFROMFD;
10238 break;
10239
10240 case '>':
10241 np->type = NFROMTO;
10242 break;
10243
10244 default:
10245 np->type = NFROM;
10246 pungetc();
10247 break;
10248 }
Eric Andersencb57d552001-06-28 07:25:16 +000010249 }
Eric Andersenc470f442003-07-28 09:56:35 +000010250 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010251 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010252 redirnode = np;
10253 goto parseredir_return;
10254}
Eric Andersencb57d552001-06-28 07:25:16 +000010255
Eric Andersencb57d552001-06-28 07:25:16 +000010256/*
10257 * Parse a substitution. At this point, we have read the dollar sign
10258 * and nothing else.
10259 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010260
10261/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10262 * (assuming ascii char codes, as the original implementation did) */
10263#define is_special(c) \
10264 ((((unsigned int)c) - 33 < 32) \
10265 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010266parsesub: {
10267 int subtype;
10268 int typeloc;
10269 int flags;
10270 char *p;
10271 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010272
Eric Andersenc470f442003-07-28 09:56:35 +000010273 c = pgetc();
10274 if (
10275 c <= PEOA_OR_PEOF ||
10276 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10277 ) {
10278 USTPUTC('$', out);
10279 pungetc();
10280 } else if (c == '(') { /* $(command) or $((arith)) */
10281 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010282#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010283 PARSEARITH();
10284#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010285 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010286#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010287 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010288 pungetc();
10289 PARSEBACKQNEW();
10290 }
10291 } else {
10292 USTPUTC(CTLVAR, out);
10293 typeloc = out - (char *)stackblock();
10294 USTPUTC(VSNORMAL, out);
10295 subtype = VSNORMAL;
10296 if (c == '{') {
10297 c = pgetc();
10298 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010299 c = pgetc();
10300 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010301 c = '#';
10302 else
10303 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010304 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010305 subtype = 0;
10306 }
10307 if (c > PEOA_OR_PEOF && is_name(c)) {
10308 do {
10309 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010310 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010311 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010312 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010313 do {
10314 STPUTC(c, out);
10315 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010316 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010317 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010318 USTPUTC(c, out);
10319 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010320 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010321 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010322
Eric Andersenc470f442003-07-28 09:56:35 +000010323 STPUTC('=', out);
10324 flags = 0;
10325 if (subtype == 0) {
10326 switch (c) {
10327 case ':':
10328 flags = VSNUL;
10329 c = pgetc();
10330 /*FALLTHROUGH*/
10331 default:
10332 p = strchr(types, c);
10333 if (p == NULL)
10334 goto badsub;
10335 subtype = p - types + VSNORMAL;
10336 break;
10337 case '%':
10338 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010339 {
10340 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010341 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010342 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010343 c = pgetc();
10344 if (c == cc)
10345 subtype++;
10346 else
10347 pungetc();
10348 break;
10349 }
10350 }
Eric Andersenc470f442003-07-28 09:56:35 +000010351 } else {
10352 pungetc();
10353 }
10354 if (dblquote || arinest)
10355 flags |= VSQUOTE;
10356 *((char *)stackblock() + typeloc) = subtype | flags;
10357 if (subtype != VSNORMAL) {
10358 varnest++;
10359 if (dblquote || arinest) {
10360 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010361 }
10362 }
10363 }
Eric Andersenc470f442003-07-28 09:56:35 +000010364 goto parsesub_return;
10365}
Eric Andersencb57d552001-06-28 07:25:16 +000010366
Eric Andersencb57d552001-06-28 07:25:16 +000010367/*
10368 * Called to parse command substitutions. Newstyle is set if the command
10369 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10370 * list of commands (passed by reference), and savelen is the number of
10371 * characters on the top of the stack which must be preserved.
10372 */
Eric Andersenc470f442003-07-28 09:56:35 +000010373parsebackq: {
10374 struct nodelist **nlpp;
10375 int savepbq;
10376 union node *n;
10377 char *volatile str;
10378 struct jmploc jmploc;
10379 struct jmploc *volatile savehandler;
10380 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010381 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010382#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010383 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010384#endif
10385
Eric Andersenc470f442003-07-28 09:56:35 +000010386 savepbq = parsebackquote;
10387 if (setjmp(jmploc.loc)) {
10388 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010389 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010390 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010391 exception_handler = savehandler;
10392 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010393 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010394 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010395 str = NULL;
10396 savelen = out - (char *)stackblock();
10397 if (savelen > 0) {
10398 str = ckmalloc(savelen);
10399 memcpy(str, stackblock(), savelen);
10400 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010401 savehandler = exception_handler;
10402 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010403 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010404 if (oldstyle) {
10405 /* We must read until the closing backquote, giving special
10406 treatment to some slashes, and then push the string and
10407 reread it as input, interpreting it normally. */
10408 char *pout;
10409 int pc;
10410 size_t psavelen;
10411 char *pstr;
10412
10413
10414 STARTSTACKSTR(pout);
10415 for (;;) {
10416 if (needprompt) {
10417 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010418 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010419 pc = pgetc();
10420 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010421 case '`':
10422 goto done;
10423
10424 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010425 pc = pgetc();
10426 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010427 plinno++;
10428 if (doprompt)
10429 setprompt(2);
10430 /*
10431 * If eating a newline, avoid putting
10432 * the newline into the new character
10433 * stream (via the STPUTC after the
10434 * switch).
10435 */
10436 continue;
10437 }
10438 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010439 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010440 STPUTC('\\', pout);
10441 if (pc > PEOA_OR_PEOF) {
10442 break;
10443 }
10444 /* fall through */
10445
10446 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010447#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010448 case PEOA:
10449#endif
10450 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010451 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010452
10453 case '\n':
10454 plinno++;
10455 needprompt = doprompt;
10456 break;
10457
10458 default:
10459 break;
10460 }
10461 STPUTC(pc, pout);
10462 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010463 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010464 STPUTC('\0', pout);
10465 psavelen = pout - (char *)stackblock();
10466 if (psavelen > 0) {
10467 pstr = grabstackstr(pout);
10468 setinputstring(pstr);
10469 }
10470 }
10471 nlpp = &bqlist;
10472 while (*nlpp)
10473 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010474 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010475 (*nlpp)->next = NULL;
10476 parsebackquote = oldstyle;
10477
10478 if (oldstyle) {
10479 saveprompt = doprompt;
10480 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010481 }
10482
Eric Andersenc470f442003-07-28 09:56:35 +000010483 n = list(2);
10484
10485 if (oldstyle)
10486 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010487 else if (readtoken() != TRP)
10488 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010489
10490 (*nlpp)->n = n;
10491 if (oldstyle) {
10492 /*
10493 * Start reading from old file again, ignoring any pushed back
10494 * tokens left from the backquote parsing
10495 */
10496 popfile();
10497 tokpushback = 0;
10498 }
10499 while (stackblocksize() <= savelen)
10500 growstackblock();
10501 STARTSTACKSTR(out);
10502 if (str) {
10503 memcpy(out, str, savelen);
10504 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010505 INT_OFF;
10506 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010507 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010508 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010509 }
10510 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010511 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010512 if (arinest || dblquote)
10513 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10514 else
10515 USTPUTC(CTLBACKQ, out);
10516 if (oldstyle)
10517 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010518 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010519}
10520
Denis Vlasenko131ae172007-02-18 13:00:19 +000010521#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010522/*
10523 * Parse an arithmetic expansion (indicate start of one and set state)
10524 */
Eric Andersenc470f442003-07-28 09:56:35 +000010525parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010526 if (++arinest == 1) {
10527 prevsyntax = syntax;
10528 syntax = ARISYNTAX;
10529 USTPUTC(CTLARI, out);
10530 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010531 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010532 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010533 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010534 } else {
10535 /*
10536 * we collapse embedded arithmetic expansion to
10537 * parenthesis, which should be equivalent
10538 */
10539 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010540 }
Eric Andersenc470f442003-07-28 09:56:35 +000010541 goto parsearith_return;
10542}
10543#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010544
Eric Andersenc470f442003-07-28 09:56:35 +000010545} /* end of readtoken */
10546
Eric Andersencb57d552001-06-28 07:25:16 +000010547/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010548 * Read the next input token.
10549 * If the token is a word, we set backquotelist to the list of cmds in
10550 * backquotes. We set quoteflag to true if any part of the word was
10551 * quoted.
10552 * If the token is TREDIR, then we set redirnode to a structure containing
10553 * the redirection.
10554 * In all cases, the variable startlinno is set to the number of the line
10555 * on which the token starts.
10556 *
10557 * [Change comment: here documents and internal procedures]
10558 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10559 * word parsing code into a separate routine. In this case, readtoken
10560 * doesn't need to have any internal procedures, but parseword does.
10561 * We could also make parseoperator in essence the main routine, and
10562 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010563 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010564#define NEW_xxreadtoken
10565#ifdef NEW_xxreadtoken
10566/* singles must be first! */
10567static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010568
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010569static const char xxreadtoken_tokens[] = {
10570 TNL, TLP, TRP, /* only single occurrence allowed */
10571 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10572 TEOF, /* corresponds to trailing nul */
10573 TAND, TOR, TENDCASE, /* if double occurrence */
10574};
10575
10576#define xxreadtoken_doubles \
10577 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10578#define xxreadtoken_singles \
10579 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10580
10581static int
10582xxreadtoken(void)
10583{
10584 int c;
10585
10586 if (tokpushback) {
10587 tokpushback = 0;
10588 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010589 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010590 if (needprompt) {
10591 setprompt(2);
10592 }
10593 startlinno = plinno;
10594 for (;;) { /* until token or start of word found */
10595 c = pgetc_macro();
10596
10597 if ((c != ' ') && (c != '\t')
10598#if ENABLE_ASH_ALIAS
10599 && (c != PEOA)
10600#endif
10601 ) {
10602 if (c == '#') {
10603 while ((c = pgetc()) != '\n' && c != PEOF);
10604 pungetc();
10605 } else if (c == '\\') {
10606 if (pgetc() != '\n') {
10607 pungetc();
10608 goto READTOKEN1;
10609 }
10610 startlinno = ++plinno;
10611 if (doprompt)
10612 setprompt(2);
10613 } else {
10614 const char *p
10615 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10616
10617 if (c != PEOF) {
10618 if (c == '\n') {
10619 plinno++;
10620 needprompt = doprompt;
10621 }
10622
10623 p = strchr(xxreadtoken_chars, c);
10624 if (p == NULL) {
10625 READTOKEN1:
10626 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10627 }
10628
10629 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10630 if (pgetc() == *p) { /* double occurrence? */
10631 p += xxreadtoken_doubles + 1;
10632 } else {
10633 pungetc();
10634 }
10635 }
10636 }
10637 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10638 }
10639 }
10640 } /* for */
10641}
10642#else
10643#define RETURN(token) return lasttoken = token
10644static int
10645xxreadtoken(void)
10646{
10647 int c;
10648
10649 if (tokpushback) {
10650 tokpushback = 0;
10651 return lasttoken;
10652 }
10653 if (needprompt) {
10654 setprompt(2);
10655 }
10656 startlinno = plinno;
10657 for (;;) { /* until token or start of word found */
10658 c = pgetc_macro();
10659 switch (c) {
10660 case ' ': case '\t':
10661#if ENABLE_ASH_ALIAS
10662 case PEOA:
10663#endif
10664 continue;
10665 case '#':
10666 while ((c = pgetc()) != '\n' && c != PEOF);
10667 pungetc();
10668 continue;
10669 case '\\':
10670 if (pgetc() == '\n') {
10671 startlinno = ++plinno;
10672 if (doprompt)
10673 setprompt(2);
10674 continue;
10675 }
10676 pungetc();
10677 goto breakloop;
10678 case '\n':
10679 plinno++;
10680 needprompt = doprompt;
10681 RETURN(TNL);
10682 case PEOF:
10683 RETURN(TEOF);
10684 case '&':
10685 if (pgetc() == '&')
10686 RETURN(TAND);
10687 pungetc();
10688 RETURN(TBACKGND);
10689 case '|':
10690 if (pgetc() == '|')
10691 RETURN(TOR);
10692 pungetc();
10693 RETURN(TPIPE);
10694 case ';':
10695 if (pgetc() == ';')
10696 RETURN(TENDCASE);
10697 pungetc();
10698 RETURN(TSEMI);
10699 case '(':
10700 RETURN(TLP);
10701 case ')':
10702 RETURN(TRP);
10703 default:
10704 goto breakloop;
10705 }
10706 }
10707 breakloop:
10708 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10709#undef RETURN
10710}
10711#endif /* NEW_xxreadtoken */
10712
10713static int
10714readtoken(void)
10715{
10716 int t;
10717#if DEBUG
10718 int alreadyseen = tokpushback;
10719#endif
10720
10721#if ENABLE_ASH_ALIAS
10722 top:
10723#endif
10724
10725 t = xxreadtoken();
10726
10727 /*
10728 * eat newlines
10729 */
10730 if (checkkwd & CHKNL) {
10731 while (t == TNL) {
10732 parseheredoc();
10733 t = xxreadtoken();
10734 }
10735 }
10736
10737 if (t != TWORD || quoteflag) {
10738 goto out;
10739 }
10740
10741 /*
10742 * check for keywords
10743 */
10744 if (checkkwd & CHKKWD) {
10745 const char *const *pp;
10746
10747 pp = findkwd(wordtext);
10748 if (pp) {
10749 lasttoken = t = pp - tokname_array;
10750 TRACE(("keyword %s recognized\n", tokname(t)));
10751 goto out;
10752 }
10753 }
10754
10755 if (checkkwd & CHKALIAS) {
10756#if ENABLE_ASH_ALIAS
10757 struct alias *ap;
10758 ap = lookupalias(wordtext, 1);
10759 if (ap != NULL) {
10760 if (*ap->val) {
10761 pushstring(ap->val, ap);
10762 }
10763 goto top;
10764 }
10765#endif
10766 }
10767 out:
10768 checkkwd = 0;
10769#if DEBUG
10770 if (!alreadyseen)
10771 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10772 else
10773 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10774#endif
10775 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010776}
10777
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010778static char
10779peektoken(void)
10780{
10781 int t;
10782
10783 t = readtoken();
10784 tokpushback++;
10785 return tokname_array[t][0];
10786}
Eric Andersencb57d552001-06-28 07:25:16 +000010787
10788/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010789 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10790 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010791 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010792static union node *
10793parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010794{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010795 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010796
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010797 tokpushback = 0;
10798 doprompt = interact;
10799 if (doprompt)
10800 setprompt(doprompt);
10801 needprompt = 0;
10802 t = readtoken();
10803 if (t == TEOF)
10804 return NEOF;
10805 if (t == TNL)
10806 return NULL;
10807 tokpushback++;
10808 return list(1);
10809}
10810
10811/*
10812 * Input any here documents.
10813 */
10814static void
10815parseheredoc(void)
10816{
10817 struct heredoc *here;
10818 union node *n;
10819
10820 here = heredoclist;
10821 heredoclist = 0;
10822
10823 while (here) {
10824 if (needprompt) {
10825 setprompt(2);
10826 }
10827 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10828 here->eofmark, here->striptabs);
10829 n = stalloc(sizeof(struct narg));
10830 n->narg.type = NARG;
10831 n->narg.next = NULL;
10832 n->narg.text = wordtext;
10833 n->narg.backquote = backquotelist;
10834 here->here->nhere.doc = n;
10835 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010836 }
Eric Andersencb57d552001-06-28 07:25:16 +000010837}
10838
10839
10840/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010841 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010842 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010843#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010844static const char *
10845expandstr(const char *ps)
10846{
10847 union node n;
10848
10849 /* XXX Fix (char *) cast. */
10850 setinputstring((char *)ps);
10851 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10852 popfile();
10853
10854 n.narg.type = NARG;
10855 n.narg.next = NULL;
10856 n.narg.text = wordtext;
10857 n.narg.backquote = backquotelist;
10858
10859 expandarg(&n, NULL, 0);
10860 return stackblock();
10861}
10862#endif
10863
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010864/*
10865 * Execute a command or commands contained in a string.
10866 */
10867static int
10868evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010869{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010870 union node *n;
10871 struct stackmark smark;
10872 int skip;
10873
10874 setinputstring(s);
10875 setstackmark(&smark);
10876
10877 skip = 0;
10878 while ((n = parsecmd(0)) != NEOF) {
10879 evaltree(n, 0);
10880 popstackmark(&smark);
10881 skip = evalskip;
10882 if (skip)
10883 break;
10884 }
10885 popfile();
10886
10887 skip &= mask;
10888 evalskip = skip;
10889 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010890}
10891
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010892/*
10893 * The eval command.
10894 */
10895static int
10896evalcmd(int argc, char **argv)
10897{
10898 char *p;
10899 char *concat;
10900 char **ap;
10901
10902 if (argc > 1) {
10903 p = argv[1];
10904 if (argc > 2) {
10905 STARTSTACKSTR(concat);
10906 ap = argv + 2;
10907 for (;;) {
10908 concat = stack_putstr(p, concat);
10909 p = *ap++;
10910 if (p == NULL)
10911 break;
10912 STPUTC(' ', concat);
10913 }
10914 STPUTC('\0', concat);
10915 p = grabstackstr(concat);
10916 }
10917 evalstring(p, ~SKIPEVAL);
10918
10919 }
10920 return exitstatus;
10921}
10922
10923/*
10924 * Read and execute commands. "Top" is nonzero for the top level command
10925 * loop; it turns on prompting if the shell is interactive.
10926 */
10927static int
10928cmdloop(int top)
10929{
10930 union node *n;
10931 struct stackmark smark;
10932 int inter;
10933 int numeof = 0;
10934
10935 TRACE(("cmdloop(%d) called\n", top));
10936 for (;;) {
10937 int skip;
10938
10939 setstackmark(&smark);
10940#if JOBS
10941 if (jobctl)
10942 showjobs(stderr, SHOW_CHANGED);
10943#endif
10944 inter = 0;
10945 if (iflag && top) {
10946 inter++;
10947#if ENABLE_ASH_MAIL
10948 chkmail();
10949#endif
10950 }
10951 n = parsecmd(inter);
10952 /* showtree(n); DEBUG */
10953 if (n == NEOF) {
10954 if (!top || numeof >= 50)
10955 break;
10956 if (!stoppedjobs()) {
10957 if (!Iflag)
10958 break;
10959 out2str("\nUse \"exit\" to leave shell.\n");
10960 }
10961 numeof++;
10962 } else if (nflag == 0) {
10963 job_warning = (job_warning == 2) ? 1 : 0;
10964 numeof = 0;
10965 evaltree(n, 0);
10966 }
10967 popstackmark(&smark);
10968 skip = evalskip;
10969
10970 if (skip) {
10971 evalskip = 0;
10972 return skip & SKIPEVAL;
10973 }
10974 }
10975 return 0;
10976}
10977
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010978/*
10979 * Take commands from a file. To be compatible we should do a path
10980 * search for the file, which is necessary to find sub-commands.
10981 */
10982static char *
10983find_dot_file(char *name)
10984{
10985 char *fullname;
10986 const char *path = pathval();
10987 struct stat statb;
10988
10989 /* don't try this for absolute or relative paths */
10990 if (strchr(name, '/'))
10991 return name;
10992
10993 while ((fullname = padvance(&path, name)) != NULL) {
10994 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10995 /*
10996 * Don't bother freeing here, since it will
10997 * be freed by the caller.
10998 */
10999 return fullname;
11000 }
11001 stunalloc(fullname);
11002 }
11003
11004 /* not found in the PATH */
11005 ash_msg_and_raise_error("%s: not found", name);
11006 /* NOTREACHED */
11007}
11008
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011009static int
11010dotcmd(int argc, char **argv)
11011{
11012 struct strlist *sp;
11013 volatile struct shparam saveparam;
11014 int status = 0;
11015
11016 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011017 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011018
11019 if (argc >= 2) { /* That's what SVR2 does */
11020 char *fullname;
11021
11022 fullname = find_dot_file(argv[1]);
11023
11024 if (argc > 2) {
11025 saveparam = shellparam;
11026 shellparam.malloc = 0;
11027 shellparam.nparam = argc - 2;
11028 shellparam.p = argv + 2;
11029 };
11030
11031 setinputfile(fullname, INPUT_PUSH_FILE);
11032 commandname = fullname;
11033 cmdloop(0);
11034 popfile();
11035
11036 if (argc > 2) {
11037 freeparam(&shellparam);
11038 shellparam = saveparam;
11039 };
11040 status = exitstatus;
11041 }
11042 return status;
11043}
11044
11045static int
11046exitcmd(int argc, char **argv)
11047{
11048 if (stoppedjobs())
11049 return 0;
11050 if (argc > 1)
11051 exitstatus = number(argv[1]);
11052 raise_exception(EXEXIT);
11053 /* NOTREACHED */
11054}
11055
11056#if ENABLE_ASH_BUILTIN_ECHO
11057static int
11058echocmd(int argc, char **argv)
11059{
11060 return bb_echo(argv);
11061}
11062#endif
11063
11064#if ENABLE_ASH_BUILTIN_TEST
11065static int
11066testcmd(int argc, char **argv)
11067{
11068 return bb_test(argc, argv);
11069}
11070#endif
11071
11072/*
11073 * Read a file containing shell functions.
11074 */
11075static void
11076readcmdfile(char *name)
11077{
11078 setinputfile(name, INPUT_PUSH_FILE);
11079 cmdloop(0);
11080 popfile();
11081}
11082
11083
Denis Vlasenkocc571512007-02-23 21:10:35 +000011084/* ============ find_command inplementation */
11085
11086/*
11087 * Resolve a command name. If you change this routine, you may have to
11088 * change the shellexec routine as well.
11089 */
11090static void
11091find_command(char *name, struct cmdentry *entry, int act, const char *path)
11092{
11093 struct tblentry *cmdp;
11094 int idx;
11095 int prev;
11096 char *fullname;
11097 struct stat statb;
11098 int e;
11099 int updatetbl;
11100 struct builtincmd *bcmd;
11101
11102 /* If name contains a slash, don't use PATH or hash table */
11103 if (strchr(name, '/') != NULL) {
11104 entry->u.index = -1;
11105 if (act & DO_ABS) {
11106 while (stat(name, &statb) < 0) {
11107#ifdef SYSV
11108 if (errno == EINTR)
11109 continue;
11110#endif
11111 entry->cmdtype = CMDUNKNOWN;
11112 return;
11113 }
11114 }
11115 entry->cmdtype = CMDNORMAL;
11116 return;
11117 }
11118
11119#if ENABLE_FEATURE_SH_STANDALONE_SHELL
11120 if (find_applet_by_name(name)) {
11121 entry->cmdtype = CMDNORMAL;
11122 entry->u.index = -1;
11123 return;
11124 }
Denis Vlasenko29e31dd2007-03-03 23:12:17 +000011125#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000011126
11127 updatetbl = (path == pathval());
11128 if (!updatetbl) {
11129 act |= DO_ALTPATH;
11130 if (strstr(path, "%builtin") != NULL)
11131 act |= DO_ALTBLTIN;
11132 }
11133
11134 /* If name is in the table, check answer will be ok */
11135 cmdp = cmdlookup(name, 0);
11136 if (cmdp != NULL) {
11137 int bit;
11138
11139 switch (cmdp->cmdtype) {
11140 default:
11141#if DEBUG
11142 abort();
11143#endif
11144 case CMDNORMAL:
11145 bit = DO_ALTPATH;
11146 break;
11147 case CMDFUNCTION:
11148 bit = DO_NOFUNC;
11149 break;
11150 case CMDBUILTIN:
11151 bit = DO_ALTBLTIN;
11152 break;
11153 }
11154 if (act & bit) {
11155 updatetbl = 0;
11156 cmdp = NULL;
11157 } else if (cmdp->rehash == 0)
11158 /* if not invalidated by cd, we're done */
11159 goto success;
11160 }
11161
11162 /* If %builtin not in path, check for builtin next */
11163 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011164 if (bcmd) {
11165 if (IS_BUILTIN_REGULAR(bcmd))
11166 goto builtin_success;
11167 if (act & DO_ALTPATH) {
11168 if (!(act & DO_ALTBLTIN))
11169 goto builtin_success;
11170 } else if (builtinloc <= 0) {
11171 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011172 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011173 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011174
11175 /* We have to search path. */
11176 prev = -1; /* where to start */
11177 if (cmdp && cmdp->rehash) { /* doing a rehash */
11178 if (cmdp->cmdtype == CMDBUILTIN)
11179 prev = builtinloc;
11180 else
11181 prev = cmdp->param.index;
11182 }
11183
11184 e = ENOENT;
11185 idx = -1;
11186 loop:
11187 while ((fullname = padvance(&path, name)) != NULL) {
11188 stunalloc(fullname);
11189 idx++;
11190 if (pathopt) {
11191 if (prefix(pathopt, "builtin")) {
11192 if (bcmd)
11193 goto builtin_success;
11194 continue;
11195 } else if (!(act & DO_NOFUNC) &&
11196 prefix(pathopt, "func")) {
11197 /* handled below */
11198 } else {
11199 /* ignore unimplemented options */
11200 continue;
11201 }
11202 }
11203 /* if rehash, don't redo absolute path names */
11204 if (fullname[0] == '/' && idx <= prev) {
11205 if (idx < prev)
11206 continue;
11207 TRACE(("searchexec \"%s\": no change\n", name));
11208 goto success;
11209 }
11210 while (stat(fullname, &statb) < 0) {
11211#ifdef SYSV
11212 if (errno == EINTR)
11213 continue;
11214#endif
11215 if (errno != ENOENT && errno != ENOTDIR)
11216 e = errno;
11217 goto loop;
11218 }
11219 e = EACCES; /* if we fail, this will be the error */
11220 if (!S_ISREG(statb.st_mode))
11221 continue;
11222 if (pathopt) { /* this is a %func directory */
11223 stalloc(strlen(fullname) + 1);
11224 readcmdfile(fullname);
11225 cmdp = cmdlookup(name, 0);
11226 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11227 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11228 stunalloc(fullname);
11229 goto success;
11230 }
11231 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11232 if (!updatetbl) {
11233 entry->cmdtype = CMDNORMAL;
11234 entry->u.index = idx;
11235 return;
11236 }
11237 INT_OFF;
11238 cmdp = cmdlookup(name, 1);
11239 cmdp->cmdtype = CMDNORMAL;
11240 cmdp->param.index = idx;
11241 INT_ON;
11242 goto success;
11243 }
11244
11245 /* We failed. If there was an entry for this command, delete it */
11246 if (cmdp && updatetbl)
11247 delete_cmd_entry();
11248 if (act & DO_ERR)
11249 ash_msg("%s: %s", name, errmsg(e, "not found"));
11250 entry->cmdtype = CMDUNKNOWN;
11251 return;
11252
11253 builtin_success:
11254 if (!updatetbl) {
11255 entry->cmdtype = CMDBUILTIN;
11256 entry->u.cmd = bcmd;
11257 return;
11258 }
11259 INT_OFF;
11260 cmdp = cmdlookup(name, 1);
11261 cmdp->cmdtype = CMDBUILTIN;
11262 cmdp->param.cmd = bcmd;
11263 INT_ON;
11264 success:
11265 cmdp->rehash = 0;
11266 entry->cmdtype = cmdp->cmdtype;
11267 entry->u = cmdp->param;
11268}
11269
11270
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011271/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011272
Eric Andersencb57d552001-06-28 07:25:16 +000011273/*
Eric Andersencb57d552001-06-28 07:25:16 +000011274 * The trap builtin.
11275 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011276static int
Eric Andersenc470f442003-07-28 09:56:35 +000011277trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011278{
11279 char *action;
11280 char **ap;
11281 int signo;
11282
Eric Andersenc470f442003-07-28 09:56:35 +000011283 nextopt(nullstr);
11284 ap = argptr;
11285 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011286 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011287 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011288 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011289
Rob Landleyc9c1a412006-07-12 19:17:55 +000011290 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011291 out1fmt("trap -- %s %s\n",
11292 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011293 }
11294 }
11295 return 0;
11296 }
Eric Andersenc470f442003-07-28 09:56:35 +000011297 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011298 action = NULL;
11299 else
11300 action = *ap++;
11301 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011302 signo = get_signum(*ap);
11303 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011304 ash_msg_and_raise_error("%s: bad trap", *ap);
11305 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011306 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011307 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011308 action = NULL;
11309 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011310 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011311 }
Eric Andersenc470f442003-07-28 09:56:35 +000011312 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011313 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011314 trap[signo] = action;
11315 if (signo != 0)
11316 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011317 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011318 ap++;
11319 }
11320 return 0;
11321}
11322
Eric Andersenc470f442003-07-28 09:56:35 +000011323
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011324/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011325
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011326#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011327/*
11328 * Lists available builtins
11329 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011330static int
11331helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011332{
11333 int col, i;
11334
11335 out1fmt("\nBuilt-in commands:\n-------------------\n");
11336 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11337 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011338 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011339 if (col > 60) {
11340 out1fmt("\n");
11341 col = 0;
11342 }
11343 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011344#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011345 for (i = 0; i < NUM_APPLETS; i++) {
11346 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11347 if (col > 60) {
11348 out1fmt("\n");
11349 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011350 }
11351 }
11352#endif
11353 out1fmt("\n\n");
11354 return EXIT_SUCCESS;
11355}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011356#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011357
Eric Andersencb57d552001-06-28 07:25:16 +000011358/*
Eric Andersencb57d552001-06-28 07:25:16 +000011359 * The export and readonly commands.
11360 */
Eric Andersenc470f442003-07-28 09:56:35 +000011361static int
11362exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011363{
11364 struct var *vp;
11365 char *name;
11366 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011367 char **aptr;
11368 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011369
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011370 if (nextopt("p") != 'p') {
11371 aptr = argptr;
11372 name = *aptr;
11373 if (name) {
11374 do {
11375 p = strchr(name, '=');
11376 if (p != NULL) {
11377 p++;
11378 } else {
11379 vp = *findvar(hashvar(name), name);
11380 if (vp) {
11381 vp->flags |= flag;
11382 continue;
11383 }
Eric Andersencb57d552001-06-28 07:25:16 +000011384 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011385 setvar(name, p, flag);
11386 } while ((name = *++aptr) != NULL);
11387 return 0;
11388 }
Eric Andersencb57d552001-06-28 07:25:16 +000011389 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011390 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011391 return 0;
11392}
11393
Eric Andersencb57d552001-06-28 07:25:16 +000011394/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011395 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011396 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011397static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011398unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011399{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011400 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011401
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011402 cmdp = cmdlookup(name, 0);
11403 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11404 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011405}
11406
Eric Andersencb57d552001-06-28 07:25:16 +000011407/*
Eric Andersencb57d552001-06-28 07:25:16 +000011408 * The unset builtin command. We unset the function before we unset the
11409 * variable to allow a function to be unset when there is a readonly variable
11410 * with the same name.
11411 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011412static int
Eric Andersenc470f442003-07-28 09:56:35 +000011413unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011414{
11415 char **ap;
11416 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011417 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011418 int ret = 0;
11419
11420 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011421 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011422 }
Eric Andersencb57d552001-06-28 07:25:16 +000011423
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011424 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011425 if (flag != 'f') {
11426 i = unsetvar(*ap);
11427 ret |= i;
11428 if (!(i & 2))
11429 continue;
11430 }
11431 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011432 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011433 }
Eric Andersenc470f442003-07-28 09:56:35 +000011434 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011435}
11436
11437
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011438/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011439
Eric Andersenc470f442003-07-28 09:56:35 +000011440#include <sys/times.h>
11441
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011442static const unsigned char timescmd_str[] = {
11443 ' ', offsetof(struct tms, tms_utime),
11444 '\n', offsetof(struct tms, tms_stime),
11445 ' ', offsetof(struct tms, tms_cutime),
11446 '\n', offsetof(struct tms, tms_cstime),
11447 0
11448};
Eric Andersencb57d552001-06-28 07:25:16 +000011449
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011450static int
11451timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011452{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011453 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011454 const unsigned char *p;
11455 struct tms buf;
11456
11457 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011458 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011459
11460 p = timescmd_str;
11461 do {
11462 t = *(clock_t *)(((char *) &buf) + p[1]);
11463 s = t / clk_tck;
11464 out1fmt("%ldm%ld.%.3lds%c",
11465 s/60, s%60,
11466 ((t - s * clk_tck) * 1000) / clk_tck,
11467 p[0]);
11468 } while (*(p += 2));
11469
Eric Andersencb57d552001-06-28 07:25:16 +000011470 return 0;
11471}
11472
Denis Vlasenko131ae172007-02-18 13:00:19 +000011473#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011474static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011475dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011476{
Eric Andersened9ecf72004-06-22 08:29:45 +000011477 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011478 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011479
Denis Vlasenkob012b102007-02-19 22:43:01 +000011480 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011481 result = arith(s, &errcode);
11482 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011483 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011484 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011485 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011486 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011487 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011488 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011489 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011490 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011491 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011492
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011493 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011494}
Eric Andersenc470f442003-07-28 09:56:35 +000011495
Eric Andersenc470f442003-07-28 09:56:35 +000011496/*
Eric Andersen90898442003-08-06 11:20:52 +000011497 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11498 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11499 *
11500 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011501 */
11502static int
Eric Andersen90898442003-08-06 11:20:52 +000011503letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011504{
Eric Andersenc470f442003-07-28 09:56:35 +000011505 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011506 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011507
Eric Andersen90898442003-08-06 11:20:52 +000011508 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011509 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011510 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011511 for (ap = argv + 1; *ap; ap++) {
11512 i = dash_arith(*ap);
11513 }
Eric Andersenc470f442003-07-28 09:56:35 +000011514
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011515 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011516}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011517#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011518
Eric Andersenc470f442003-07-28 09:56:35 +000011519
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011520/* ============ miscbltin.c
11521 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011522 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011523 */
11524
11525#undef rflag
11526
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011527#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011528typedef enum __rlimit_resource rlim_t;
11529#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011530
Eric Andersenc470f442003-07-28 09:56:35 +000011531/*
11532 * The read builtin. The -e option causes backslashes to escape the
11533 * following character.
11534 *
11535 * This uses unbuffered input, which may be avoidable in some cases.
11536 */
Eric Andersenc470f442003-07-28 09:56:35 +000011537static int
11538readcmd(int argc, char **argv)
11539{
11540 char **ap;
11541 int backslash;
11542 char c;
11543 int rflag;
11544 char *prompt;
11545 const char *ifs;
11546 char *p;
11547 int startword;
11548 int status;
11549 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011550#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011551 int nch_flag = 0;
11552 int nchars = 0;
11553 int silent = 0;
11554 struct termios tty, old_tty;
11555#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011556#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011557 fd_set set;
11558 struct timeval ts;
11559
11560 ts.tv_sec = ts.tv_usec = 0;
11561#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011562
11563 rflag = 0;
11564 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011565#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011566 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011567#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011568 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011569#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011570 while ((i = nextopt("p:rt:")) != '\0')
11571#else
11572 while ((i = nextopt("p:r")) != '\0')
11573#endif
11574 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011575 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011576 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011577 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011578 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011579#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011580 case 'n':
11581 nchars = strtol(optionarg, &p, 10);
11582 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011583 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011584 nch_flag = (nchars > 0);
11585 break;
11586 case 's':
11587 silent = 1;
11588 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011589#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011590#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011591 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011592 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011593 ts.tv_usec = 0;
11594 if (*p == '.') {
11595 char *p2;
11596 if (*++p) {
11597 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011598 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011599 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011600 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011601 scale = p2 - p;
11602 /* normalize to usec */
11603 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011604 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011605 while (scale++ < 6)
11606 ts.tv_usec *= 10;
11607 }
11608 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011609 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011610 }
11611 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011612 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011613 break;
11614#endif
11615 case 'r':
11616 rflag = 1;
11617 break;
11618 default:
11619 break;
11620 }
Eric Andersenc470f442003-07-28 09:56:35 +000011621 }
11622 if (prompt && isatty(0)) {
11623 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011624 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011625 ap = argptr;
11626 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011627 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011628 ifs = bltinlookup("IFS");
11629 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011630 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011631#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011632 if (nch_flag || silent) {
11633 tcgetattr(0, &tty);
11634 old_tty = tty;
11635 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011636 tty.c_lflag &= ~ICANON;
11637 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011638 }
11639 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011640 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011641
11642 }
11643 tcsetattr(0, TCSANOW, &tty);
11644 }
11645#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011646#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011647 if (ts.tv_sec || ts.tv_usec) {
11648 FD_ZERO (&set);
11649 FD_SET (0, &set);
11650
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011651 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011652 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011653#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011654 if (nch_flag)
11655 tcsetattr(0, TCSANOW, &old_tty);
11656#endif
11657 return 1;
11658 }
11659 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011660#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011661 status = 0;
11662 startword = 1;
11663 backslash = 0;
11664 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011665#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011666 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011667#else
11668 for (;;)
11669#endif
11670 {
Eric Andersenc470f442003-07-28 09:56:35 +000011671 if (read(0, &c, 1) != 1) {
11672 status = 1;
11673 break;
11674 }
11675 if (c == '\0')
11676 continue;
11677 if (backslash) {
11678 backslash = 0;
11679 if (c != '\n')
11680 goto put;
11681 continue;
11682 }
11683 if (!rflag && c == '\\') {
11684 backslash++;
11685 continue;
11686 }
11687 if (c == '\n')
11688 break;
11689 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11690 continue;
11691 }
11692 startword = 0;
11693 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11694 STACKSTRNUL(p);
11695 setvar(*ap, stackblock(), 0);
11696 ap++;
11697 startword = 1;
11698 STARTSTACKSTR(p);
11699 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011700 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011701 STPUTC(c, p);
11702 }
11703 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011704#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011705 if (nch_flag || silent)
11706 tcsetattr(0, TCSANOW, &old_tty);
11707#endif
11708
Eric Andersenc470f442003-07-28 09:56:35 +000011709 STACKSTRNUL(p);
11710 /* Remove trailing blanks */
11711 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11712 *p = '\0';
11713 setvar(*ap, stackblock(), 0);
11714 while (*++ap != NULL)
11715 setvar(*ap, nullstr, 0);
11716 return status;
11717}
11718
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011719static int
11720umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011721{
11722 static const char permuser[3] = "ugo";
11723 static const char permmode[3] = "rwx";
11724 static const short int permmask[] = {
11725 S_IRUSR, S_IWUSR, S_IXUSR,
11726 S_IRGRP, S_IWGRP, S_IXGRP,
11727 S_IROTH, S_IWOTH, S_IXOTH
11728 };
11729
11730 char *ap;
11731 mode_t mask;
11732 int i;
11733 int symbolic_mode = 0;
11734
11735 while (nextopt("S") != '\0') {
11736 symbolic_mode = 1;
11737 }
11738
Denis Vlasenkob012b102007-02-19 22:43:01 +000011739 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011740 mask = umask(0);
11741 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011742 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011743
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011744 ap = *argptr;
11745 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011746 if (symbolic_mode) {
11747 char buf[18];
11748 char *p = buf;
11749
11750 for (i = 0; i < 3; i++) {
11751 int j;
11752
11753 *p++ = permuser[i];
11754 *p++ = '=';
11755 for (j = 0; j < 3; j++) {
11756 if ((mask & permmask[3 * i + j]) == 0) {
11757 *p++ = permmode[j];
11758 }
11759 }
11760 *p++ = ',';
11761 }
11762 *--p = 0;
11763 puts(buf);
11764 } else {
11765 out1fmt("%.4o\n", mask);
11766 }
11767 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011768 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011769 mask = 0;
11770 do {
11771 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011772 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011773 mask = (mask << 3) + (*ap - '0');
11774 } while (*++ap != '\0');
11775 umask(mask);
11776 } else {
11777 mask = ~mask & 0777;
11778 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011779 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011780 }
11781 umask(~mask & 0777);
11782 }
11783 }
11784 return 0;
11785}
11786
11787/*
11788 * ulimit builtin
11789 *
11790 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11791 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11792 * ash by J.T. Conklin.
11793 *
11794 * Public domain.
11795 */
11796
11797struct limits {
11798 const char *name;
11799 int cmd;
11800 int factor; /* multiply by to get rlim_{cur,max} values */
11801 char option;
11802};
11803
11804static const struct limits limits[] = {
11805#ifdef RLIMIT_CPU
11806 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11807#endif
11808#ifdef RLIMIT_FSIZE
11809 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11810#endif
11811#ifdef RLIMIT_DATA
11812 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11813#endif
11814#ifdef RLIMIT_STACK
11815 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11816#endif
11817#ifdef RLIMIT_CORE
11818 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11819#endif
11820#ifdef RLIMIT_RSS
11821 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11822#endif
11823#ifdef RLIMIT_MEMLOCK
11824 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11825#endif
11826#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011827 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011828#endif
11829#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011830 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011831#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011832#ifdef RLIMIT_AS
11833 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011834#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011835#ifdef RLIMIT_LOCKS
11836 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011837#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011838 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011839};
11840
Glenn L McGrath76620622004-01-13 10:19:37 +000011841enum limtype { SOFT = 0x1, HARD = 0x2 };
11842
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011843static void
11844printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011845 const struct limits *l)
11846{
11847 rlim_t val;
11848
11849 val = limit->rlim_max;
11850 if (how & SOFT)
11851 val = limit->rlim_cur;
11852
11853 if (val == RLIM_INFINITY)
11854 out1fmt("unlimited\n");
11855 else {
11856 val /= l->factor;
11857 out1fmt("%lld\n", (long long) val);
11858 }
11859}
11860
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011861static int
Eric Andersenc470f442003-07-28 09:56:35 +000011862ulimitcmd(int argc, char **argv)
11863{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011864 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011865 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011866 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011867 const struct limits *l;
11868 int set, all = 0;
11869 int optc, what;
11870 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011871
11872 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011873 while ((optc = nextopt("HSa"
11874#ifdef RLIMIT_CPU
11875 "t"
11876#endif
11877#ifdef RLIMIT_FSIZE
11878 "f"
11879#endif
11880#ifdef RLIMIT_DATA
11881 "d"
11882#endif
11883#ifdef RLIMIT_STACK
11884 "s"
11885#endif
11886#ifdef RLIMIT_CORE
11887 "c"
11888#endif
11889#ifdef RLIMIT_RSS
11890 "m"
11891#endif
11892#ifdef RLIMIT_MEMLOCK
11893 "l"
11894#endif
11895#ifdef RLIMIT_NPROC
11896 "p"
11897#endif
11898#ifdef RLIMIT_NOFILE
11899 "n"
11900#endif
11901#ifdef RLIMIT_AS
11902 "v"
11903#endif
11904#ifdef RLIMIT_LOCKS
11905 "w"
11906#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011907 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011908 switch (optc) {
11909 case 'H':
11910 how = HARD;
11911 break;
11912 case 'S':
11913 how = SOFT;
11914 break;
11915 case 'a':
11916 all = 1;
11917 break;
11918 default:
11919 what = optc;
11920 }
11921
Glenn L McGrath76620622004-01-13 10:19:37 +000011922 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011923 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011924
11925 set = *argptr ? 1 : 0;
11926 if (set) {
11927 char *p = *argptr;
11928
11929 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011930 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011931 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011932 val = RLIM_INFINITY;
11933 else {
11934 val = (rlim_t) 0;
11935
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011936 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011937 val = (val * 10) + (long)(c - '0');
11938 if (val < (rlim_t) 0)
11939 break;
11940 }
11941 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011942 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011943 val *= l->factor;
11944 }
11945 }
11946 if (all) {
11947 for (l = limits; l->name; l++) {
11948 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011949 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011950 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011951 }
11952 return 0;
11953 }
11954
11955 getrlimit(l->cmd, &limit);
11956 if (set) {
11957 if (how & HARD)
11958 limit.rlim_max = val;
11959 if (how & SOFT)
11960 limit.rlim_cur = val;
11961 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011962 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011963 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011964 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011965 }
11966 return 0;
11967}
11968
Eric Andersen90898442003-08-06 11:20:52 +000011969
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011970/* ============ Math support */
11971
Denis Vlasenko131ae172007-02-18 13:00:19 +000011972#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011973
11974/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11975
11976 Permission is hereby granted, free of charge, to any person obtaining
11977 a copy of this software and associated documentation files (the
11978 "Software"), to deal in the Software without restriction, including
11979 without limitation the rights to use, copy, modify, merge, publish,
11980 distribute, sublicense, and/or sell copies of the Software, and to
11981 permit persons to whom the Software is furnished to do so, subject to
11982 the following conditions:
11983
11984 The above copyright notice and this permission notice shall be
11985 included in all copies or substantial portions of the Software.
11986
11987 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11988 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11989 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11990 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11991 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11992 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11993 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11994*/
11995
11996/* This is my infix parser/evaluator. It is optimized for size, intended
11997 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011998 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011999 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012000 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012001 * be that which POSIX specifies for shells. */
12002
12003/* The code uses a simple two-stack algorithm. See
12004 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012005 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012006 * this is based (this code differs in that it applies operators immediately
12007 * to the stack instead of adding them to a queue to end up with an
12008 * expression). */
12009
12010/* To use the routine, call it with an expression string and error return
12011 * pointer */
12012
12013/*
12014 * Aug 24, 2001 Manuel Novoa III
12015 *
12016 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12017 *
12018 * 1) In arith_apply():
12019 * a) Cached values of *numptr and &(numptr[-1]).
12020 * b) Removed redundant test for zero denominator.
12021 *
12022 * 2) In arith():
12023 * a) Eliminated redundant code for processing operator tokens by moving
12024 * to a table-based implementation. Also folded handling of parens
12025 * into the table.
12026 * b) Combined all 3 loops which called arith_apply to reduce generated
12027 * code size at the cost of speed.
12028 *
12029 * 3) The following expressions were treated as valid by the original code:
12030 * 1() , 0! , 1 ( *3 ) .
12031 * These bugs have been fixed by internally enclosing the expression in
12032 * parens and then checking that all binary ops and right parens are
12033 * preceded by a valid expression (NUM_TOKEN).
12034 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012035 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012036 * ctype's isspace() if it is used by another busybox applet or if additional
12037 * whitespace chars should be considered. Look below the "#include"s for a
12038 * precompiler test.
12039 */
12040
12041/*
12042 * Aug 26, 2001 Manuel Novoa III
12043 *
12044 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12045 *
12046 * Merge in Aaron's comments previously posted to the busybox list,
12047 * modified slightly to take account of my changes to the code.
12048 *
12049 */
12050
12051/*
12052 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12053 *
12054 * - allow access to variable,
12055 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12056 * - realize assign syntax (VAR=expr, +=, *= etc)
12057 * - realize exponentiation (** operator)
12058 * - realize comma separated - expr, expr
12059 * - realise ++expr --expr expr++ expr--
12060 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012061 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012062 * - was restored loses XOR operator
12063 * - remove one goto label, added three ;-)
12064 * - protect $((num num)) as true zero expr (Manuel`s error)
12065 * - always use special isspace(), see comment from bash ;-)
12066 */
12067
Eric Andersen90898442003-08-06 11:20:52 +000012068#define arith_isspace(arithval) \
12069 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12070
Eric Andersen90898442003-08-06 11:20:52 +000012071typedef unsigned char operator;
12072
12073/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012074 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012075 * precedence. The ID portion is so that multiple operators can have the
12076 * same precedence, ensuring that the leftmost one is evaluated first.
12077 * Consider * and /. */
12078
12079#define tok_decl(prec,id) (((id)<<5)|(prec))
12080#define PREC(op) ((op) & 0x1F)
12081
12082#define TOK_LPAREN tok_decl(0,0)
12083
12084#define TOK_COMMA tok_decl(1,0)
12085
12086#define TOK_ASSIGN tok_decl(2,0)
12087#define TOK_AND_ASSIGN tok_decl(2,1)
12088#define TOK_OR_ASSIGN tok_decl(2,2)
12089#define TOK_XOR_ASSIGN tok_decl(2,3)
12090#define TOK_PLUS_ASSIGN tok_decl(2,4)
12091#define TOK_MINUS_ASSIGN tok_decl(2,5)
12092#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12093#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12094
12095#define TOK_MUL_ASSIGN tok_decl(3,0)
12096#define TOK_DIV_ASSIGN tok_decl(3,1)
12097#define TOK_REM_ASSIGN tok_decl(3,2)
12098
12099/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012100#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012101
12102/* conditional is right associativity too */
12103#define TOK_CONDITIONAL tok_decl(4,0)
12104#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12105
12106#define TOK_OR tok_decl(5,0)
12107
12108#define TOK_AND tok_decl(6,0)
12109
12110#define TOK_BOR tok_decl(7,0)
12111
12112#define TOK_BXOR tok_decl(8,0)
12113
12114#define TOK_BAND tok_decl(9,0)
12115
12116#define TOK_EQ tok_decl(10,0)
12117#define TOK_NE tok_decl(10,1)
12118
12119#define TOK_LT tok_decl(11,0)
12120#define TOK_GT tok_decl(11,1)
12121#define TOK_GE tok_decl(11,2)
12122#define TOK_LE tok_decl(11,3)
12123
12124#define TOK_LSHIFT tok_decl(12,0)
12125#define TOK_RSHIFT tok_decl(12,1)
12126
12127#define TOK_ADD tok_decl(13,0)
12128#define TOK_SUB tok_decl(13,1)
12129
12130#define TOK_MUL tok_decl(14,0)
12131#define TOK_DIV tok_decl(14,1)
12132#define TOK_REM tok_decl(14,2)
12133
12134/* exponent is right associativity */
12135#define TOK_EXPONENT tok_decl(15,1)
12136
12137/* For now unary operators. */
12138#define UNARYPREC 16
12139#define TOK_BNOT tok_decl(UNARYPREC,0)
12140#define TOK_NOT tok_decl(UNARYPREC,1)
12141
12142#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12143#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12144
12145#define PREC_PRE (UNARYPREC+2)
12146
12147#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12148#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12149
12150#define PREC_POST (UNARYPREC+3)
12151
12152#define TOK_POST_INC tok_decl(PREC_POST, 0)
12153#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12154
12155#define SPEC_PREC (UNARYPREC+4)
12156
12157#define TOK_NUM tok_decl(SPEC_PREC, 0)
12158#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12159
12160#define NUMPTR (*numstackptr)
12161
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012162static int
12163tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012164{
12165 operator prec = PREC(op);
12166
12167 convert_prec_is_assing(prec);
12168 return (prec == PREC(TOK_ASSIGN) ||
12169 prec == PREC_PRE || prec == PREC_POST);
12170}
12171
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012172static int
12173is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012174{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012175 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12176 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012177}
12178
Eric Andersen90898442003-08-06 11:20:52 +000012179typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012180 arith_t val;
12181 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012182 char contidional_second_val_initialized;
12183 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012184 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012185} v_n_t;
12186
Eric Andersen90898442003-08-06 11:20:52 +000012187typedef struct CHK_VAR_RECURSIVE_LOOPED {
12188 const char *var;
12189 struct CHK_VAR_RECURSIVE_LOOPED *next;
12190} chk_var_recursive_looped_t;
12191
12192static chk_var_recursive_looped_t *prev_chk_var_recursive;
12193
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012194static int
12195arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012196{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012197 if (t->var) {
12198 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012199
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012200 if (p) {
12201 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012202
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012203 /* recursive try as expression */
12204 chk_var_recursive_looped_t *cur;
12205 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012206
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012207 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12208 if (strcmp(cur->var, t->var) == 0) {
12209 /* expression recursion loop detected */
12210 return -5;
12211 }
12212 }
12213 /* save current lookuped var name */
12214 cur = prev_chk_var_recursive;
12215 cur_save.var = t->var;
12216 cur_save.next = cur;
12217 prev_chk_var_recursive = &cur_save;
12218
12219 t->val = arith (p, &errcode);
12220 /* restore previous ptr after recursiving */
12221 prev_chk_var_recursive = cur;
12222 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012223 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012224 /* allow undefined var as 0 */
12225 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012226 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012227 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012228}
12229
12230/* "applying" a token means performing it on the top elements on the integer
12231 * stack. For a unary operator it will only change the top element, but a
12232 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012233static int
12234arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012235{
Eric Andersen90898442003-08-06 11:20:52 +000012236 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012237 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012238 int ret_arith_lookup_val;
12239
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012240 /* There is no operator that can work without arguments */
12241 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012242 numptr_m1 = NUMPTR - 1;
12243
12244 /* check operand is var with noninteger value */
12245 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012246 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012247 return ret_arith_lookup_val;
12248
12249 rez = numptr_m1->val;
12250 if (op == TOK_UMINUS)
12251 rez *= -1;
12252 else if (op == TOK_NOT)
12253 rez = !rez;
12254 else if (op == TOK_BNOT)
12255 rez = ~rez;
12256 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12257 rez++;
12258 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12259 rez--;
12260 else if (op != TOK_UPLUS) {
12261 /* Binary operators */
12262
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012263 /* check and binary operators need two arguments */
12264 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012265
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012266 /* ... and they pop one */
12267 --NUMPTR;
12268 numptr_val = rez;
12269 if (op == TOK_CONDITIONAL) {
12270 if (! numptr_m1->contidional_second_val_initialized) {
12271 /* protect $((expr1 ? expr2)) without ": expr" */
12272 goto err;
12273 }
12274 rez = numptr_m1->contidional_second_val;
12275 } else if (numptr_m1->contidional_second_val_initialized) {
12276 /* protect $((expr1 : expr2)) without "expr ? " */
12277 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012278 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012279 numptr_m1 = NUMPTR - 1;
12280 if (op != TOK_ASSIGN) {
12281 /* check operand is var with noninteger value for not '=' */
12282 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12283 if (ret_arith_lookup_val)
12284 return ret_arith_lookup_val;
12285 }
12286 if (op == TOK_CONDITIONAL) {
12287 numptr_m1->contidional_second_val = rez;
12288 }
12289 rez = numptr_m1->val;
12290 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012291 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012292 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012293 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012294 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012295 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012296 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012297 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012298 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012299 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012300 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012301 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012302 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012303 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012304 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012305 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012306 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012307 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012308 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012309 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012310 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012311 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012312 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012313 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012314 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012315 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012316 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012317 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012318 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012319 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012320 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012321 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012322 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012323 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012324 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012325 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012326 /* protect $((expr : expr)) without "expr ? " */
12327 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012328 }
12329 numptr_m1->contidional_second_val_initialized = op;
12330 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012331 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012332 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012333 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012334 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012335 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012336 return -3; /* exponent less than 0 */
12337 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012338 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012339
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012340 if (numptr_val)
12341 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012342 c *= rez;
12343 rez = c;
12344 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012345 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012346 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012347 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012348 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012349 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012350 rez %= numptr_val;
12351 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012352 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012353 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012354
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012355 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012356 /* Hmm, 1=2 ? */
12357 goto err;
12358 }
12359 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012360#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012361 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012362#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012363 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012364#endif
Eric Andersen90898442003-08-06 11:20:52 +000012365 setvar(numptr_m1->var, buf, 0);
12366 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012367 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012368 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012369 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012370 rez++;
12371 }
12372 numptr_m1->val = rez;
12373 /* protect geting var value, is number now */
12374 numptr_m1->var = NULL;
12375 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012376 err:
12377 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012378}
12379
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012380/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012381static const char op_tokens[] = {
12382 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12383 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12384 '<','<', 0, TOK_LSHIFT,
12385 '>','>', 0, TOK_RSHIFT,
12386 '|','|', 0, TOK_OR,
12387 '&','&', 0, TOK_AND,
12388 '!','=', 0, TOK_NE,
12389 '<','=', 0, TOK_LE,
12390 '>','=', 0, TOK_GE,
12391 '=','=', 0, TOK_EQ,
12392 '|','=', 0, TOK_OR_ASSIGN,
12393 '&','=', 0, TOK_AND_ASSIGN,
12394 '*','=', 0, TOK_MUL_ASSIGN,
12395 '/','=', 0, TOK_DIV_ASSIGN,
12396 '%','=', 0, TOK_REM_ASSIGN,
12397 '+','=', 0, TOK_PLUS_ASSIGN,
12398 '-','=', 0, TOK_MINUS_ASSIGN,
12399 '-','-', 0, TOK_POST_DEC,
12400 '^','=', 0, TOK_XOR_ASSIGN,
12401 '+','+', 0, TOK_POST_INC,
12402 '*','*', 0, TOK_EXPONENT,
12403 '!', 0, TOK_NOT,
12404 '<', 0, TOK_LT,
12405 '>', 0, TOK_GT,
12406 '=', 0, TOK_ASSIGN,
12407 '|', 0, TOK_BOR,
12408 '&', 0, TOK_BAND,
12409 '*', 0, TOK_MUL,
12410 '/', 0, TOK_DIV,
12411 '%', 0, TOK_REM,
12412 '+', 0, TOK_ADD,
12413 '-', 0, TOK_SUB,
12414 '^', 0, TOK_BXOR,
12415 /* uniq */
12416 '~', 0, TOK_BNOT,
12417 ',', 0, TOK_COMMA,
12418 '?', 0, TOK_CONDITIONAL,
12419 ':', 0, TOK_CONDITIONAL_SEP,
12420 ')', 0, TOK_RPAREN,
12421 '(', 0, TOK_LPAREN,
12422 0
12423};
12424/* ptr to ")" */
12425#define endexpression &op_tokens[sizeof(op_tokens)-7]
12426
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012427static arith_t
12428arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012429{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012430 char arithval; /* Current character under analysis */
12431 operator lasttok, op;
12432 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012433
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012434 const char *p = endexpression;
12435 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012436
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012437 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012438
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012439 /* Stack of integers */
12440 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12441 * in any given correct or incorrect expression is left as an exercise to
12442 * the reader. */
12443 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12444 *numstackptr = numstack;
12445 /* Stack of operator tokens */
12446 operator *stack = alloca((datasizes) * sizeof(operator)),
12447 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012448
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012449 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12450 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012451
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012452 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012453 arithval = *expr;
12454 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012455 if (p == endexpression) {
12456 /* Null expression. */
12457 return 0;
12458 }
12459
12460 /* This is only reached after all tokens have been extracted from the
12461 * input stream. If there are still tokens on the operator stack, they
12462 * are to be applied in order. At the end, there should be a final
12463 * result on the integer stack */
12464
12465 if (expr != endexpression + 1) {
12466 /* If we haven't done so already, */
12467 /* append a closing right paren */
12468 expr = endexpression;
12469 /* and let the loop process it. */
12470 continue;
12471 }
12472 /* At this point, we're done with the expression. */
12473 if (numstackptr != numstack+1) {
12474 /* ... but if there isn't, it's bad */
12475 err:
12476 return (*perrcode = -1);
12477 }
12478 if (numstack->var) {
12479 /* expression is $((var)) only, lookup now */
12480 errcode = arith_lookup_val(numstack);
12481 }
12482 ret:
12483 *perrcode = errcode;
12484 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012485 }
12486
Eric Andersen90898442003-08-06 11:20:52 +000012487 /* Continue processing the expression. */
12488 if (arith_isspace(arithval)) {
12489 /* Skip whitespace */
12490 goto prologue;
12491 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012492 p = endofname(expr);
12493 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012494 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012495
12496 numstackptr->var = alloca(var_name_size);
12497 safe_strncpy(numstackptr->var, expr, var_name_size);
12498 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012499 num:
Eric Andersen90898442003-08-06 11:20:52 +000012500 numstackptr->contidional_second_val_initialized = 0;
12501 numstackptr++;
12502 lasttok = TOK_NUM;
12503 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012504 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012505 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012506 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012507#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012508 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012509#else
12510 numstackptr->val = strtol(expr, (char **) &expr, 0);
12511#endif
Eric Andersen90898442003-08-06 11:20:52 +000012512 goto num;
12513 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012514 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012515 const char *o;
12516
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012517 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012518 /* strange operator not found */
12519 goto err;
12520 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012521 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012522 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012523 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012524 /* found */
12525 expr = o - 1;
12526 break;
12527 }
12528 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012529 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012530 p++;
12531 /* skip zero delim */
12532 p++;
12533 }
12534 op = p[1];
12535
12536 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012537 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12538 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012539
12540 /* Plus and minus are binary (not unary) _only_ if the last
12541 * token was as number, or a right paren (which pretends to be
12542 * a number, since it evaluates to one). Think about it.
12543 * It makes sense. */
12544 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012545 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012546 case TOK_ADD:
12547 op = TOK_UPLUS;
12548 break;
12549 case TOK_SUB:
12550 op = TOK_UMINUS;
12551 break;
12552 case TOK_POST_INC:
12553 op = TOK_PRE_INC;
12554 break;
12555 case TOK_POST_DEC:
12556 op = TOK_PRE_DEC;
12557 break;
Eric Andersen90898442003-08-06 11:20:52 +000012558 }
12559 }
12560 /* We don't want a unary operator to cause recursive descent on the
12561 * stack, because there can be many in a row and it could cause an
12562 * operator to be evaluated before its argument is pushed onto the
12563 * integer stack. */
12564 /* But for binary operators, "apply" everything on the operator
12565 * stack until we find an operator with a lesser priority than the
12566 * one we have just extracted. */
12567 /* Left paren is given the lowest priority so it will never be
12568 * "applied" in this way.
12569 * if associativity is right and priority eq, applied also skip
12570 */
12571 prec = PREC(op);
12572 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12573 /* not left paren or unary */
12574 if (lasttok != TOK_NUM) {
12575 /* binary op must be preceded by a num */
12576 goto err;
12577 }
12578 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012579 if (op == TOK_RPAREN) {
12580 /* The algorithm employed here is simple: while we don't
12581 * hit an open paren nor the bottom of the stack, pop
12582 * tokens and apply them */
12583 if (stackptr[-1] == TOK_LPAREN) {
12584 --stackptr;
12585 /* Any operator directly after a */
12586 lasttok = TOK_NUM;
12587 /* close paren should consider itself binary */
12588 goto prologue;
12589 }
12590 } else {
12591 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012592
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012593 convert_prec_is_assing(prec);
12594 convert_prec_is_assing(prev_prec);
12595 if (prev_prec < prec)
12596 break;
12597 /* check right assoc */
12598 if (prev_prec == prec && is_right_associativity(prec))
12599 break;
12600 }
12601 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12602 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012603 }
12604 if (op == TOK_RPAREN) {
12605 goto err;
12606 }
12607 }
12608
12609 /* Push this operator to the stack and remember it. */
12610 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012611 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012612 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012613 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012614}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012615#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012616
12617
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012618/* ============ main() and helpers */
12619
12620/*
12621 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012622 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012623static void exitshell(void) ATTRIBUTE_NORETURN;
12624static void
12625exitshell(void)
12626{
12627 struct jmploc loc;
12628 char *p;
12629 int status;
12630
12631 status = exitstatus;
12632 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12633 if (setjmp(loc.loc)) {
12634 if (exception == EXEXIT)
12635/* dash bug: it just does _exit(exitstatus) here
12636 * but we have to do setjobctl(0) first!
12637 * (bug is still not fixed in dash-0.5.3 - if you run dash
12638 * under Midnight Commander, on exit from dash MC is backgrounded) */
12639 status = exitstatus;
12640 goto out;
12641 }
12642 exception_handler = &loc;
12643 p = trap[0];
12644 if (p) {
12645 trap[0] = NULL;
12646 evalstring(p, 0);
12647 }
12648 flush_stdout_stderr();
12649 out:
12650 setjobctl(0);
12651 _exit(status);
12652 /* NOTREACHED */
12653}
12654
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012655static void
12656init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012657{
12658 /* from input.c: */
12659 basepf.nextc = basepf.buf = basebuf;
12660
12661 /* from trap.c: */
12662 signal(SIGCHLD, SIG_DFL);
12663
12664 /* from var.c: */
12665 {
12666 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012667 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012668 const char *p;
12669 struct stat st1, st2;
12670
12671 initvar();
12672 for (envp = environ; envp && *envp; envp++) {
12673 if (strchr(*envp, '=')) {
12674 setvareq(*envp, VEXPORT|VTEXTFIXED);
12675 }
12676 }
12677
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012678 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012679 setvar("PPID", ppid, 0);
12680
12681 p = lookupvar("PWD");
12682 if (p)
12683 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12684 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12685 p = '\0';
12686 setpwd(p, 0);
12687 }
12688}
12689
12690/*
12691 * Process the shell command line arguments.
12692 */
12693static void
12694procargs(int argc, char **argv)
12695{
12696 int i;
12697 const char *xminusc;
12698 char **xargv;
12699
12700 xargv = argv;
12701 arg0 = xargv[0];
12702 if (argc > 0)
12703 xargv++;
12704 for (i = 0; i < NOPTS; i++)
12705 optlist[i] = 2;
12706 argptr = xargv;
12707 options(1);
12708 xargv = argptr;
12709 xminusc = minusc;
12710 if (*xargv == NULL) {
12711 if (xminusc)
12712 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12713 sflag = 1;
12714 }
12715 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12716 iflag = 1;
12717 if (mflag == 2)
12718 mflag = iflag;
12719 for (i = 0; i < NOPTS; i++)
12720 if (optlist[i] == 2)
12721 optlist[i] = 0;
12722#if DEBUG == 2
12723 debug = 1;
12724#endif
12725 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12726 if (xminusc) {
12727 minusc = *xargv++;
12728 if (*xargv)
12729 goto setarg0;
12730 } else if (!sflag) {
12731 setinputfile(*xargv, 0);
12732 setarg0:
12733 arg0 = *xargv++;
12734 commandname = arg0;
12735 }
12736
12737 shellparam.p = xargv;
12738#if ENABLE_ASH_GETOPTS
12739 shellparam.optind = 1;
12740 shellparam.optoff = -1;
12741#endif
12742 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12743 while (*xargv) {
12744 shellparam.nparam++;
12745 xargv++;
12746 }
12747 optschanged();
12748}
12749
12750/*
12751 * Read /etc/profile or .profile.
12752 */
12753static void
12754read_profile(const char *name)
12755{
12756 int skip;
12757
12758 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12759 return;
12760 skip = cmdloop(0);
12761 popfile();
12762 if (skip)
12763 exitshell();
12764}
12765
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012766/*
12767 * This routine is called when an error or an interrupt occurs in an
12768 * interactive shell and control is returned to the main command loop.
12769 */
12770static void
12771reset(void)
12772{
12773 /* from eval.c: */
12774 evalskip = 0;
12775 loopnest = 0;
12776 /* from input.c: */
12777 parselleft = parsenleft = 0; /* clear input buffer */
12778 popallfiles();
12779 /* from parser.c: */
12780 tokpushback = 0;
12781 checkkwd = 0;
12782 /* from redir.c: */
12783 clearredir(0);
12784}
12785
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012786#if PROFILE
12787static short profile_buf[16384];
12788extern int etext();
12789#endif
12790
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012791/*
12792 * Main routine. We initialize things, parse the arguments, execute
12793 * profiles if we're a login shell, and then call cmdloop to execute
12794 * commands. The setjmp call sets up the location to jump to when an
12795 * exception occurs. When an exception occurs the variable "state"
12796 * is used to figure out how far we had gotten.
12797 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012798int ash_main(int argc, char **argv);
12799int ash_main(int argc, char **argv)
12800{
12801 char *shinit;
12802 volatile int state;
12803 struct jmploc jmploc;
12804 struct stackmark smark;
12805
12806#ifdef __GLIBC__
12807 dash_errno = __errno_location();
12808#endif
12809
12810#if PROFILE
12811 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12812#endif
12813
12814#if ENABLE_FEATURE_EDITING
12815 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12816#endif
12817 state = 0;
12818 if (setjmp(jmploc.loc)) {
12819 int e;
12820 int s;
12821
12822 reset();
12823
12824 e = exception;
12825 if (e == EXERROR)
12826 exitstatus = 2;
12827 s = state;
12828 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12829 exitshell();
12830
12831 if (e == EXINT) {
12832 outcslow('\n', stderr);
12833 }
12834 popstackmark(&smark);
12835 FORCE_INT_ON; /* enable interrupts */
12836 if (s == 1)
12837 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012838 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012839 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012840 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012841 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012842 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012843 }
12844 exception_handler = &jmploc;
12845#if DEBUG
12846 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012847 trace_puts("Shell args: ");
12848 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012849#endif
12850 rootpid = getpid();
12851
12852#if ENABLE_ASH_RANDOM_SUPPORT
12853 rseed = rootpid + time(NULL);
12854#endif
12855 init();
12856 setstackmark(&smark);
12857 procargs(argc, argv);
12858#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12859 if (iflag) {
12860 const char *hp = lookupvar("HISTFILE");
12861
12862 if (hp == NULL) {
12863 hp = lookupvar("HOME");
12864 if (hp != NULL) {
12865 char *defhp = concat_path_file(hp, ".ash_history");
12866 setvar("HISTFILE", defhp, 0);
12867 free(defhp);
12868 }
12869 }
12870 }
12871#endif
12872 if (argv[0] && argv[0][0] == '-')
12873 isloginsh = 1;
12874 if (isloginsh) {
12875 state = 1;
12876 read_profile("/etc/profile");
12877 state1:
12878 state = 2;
12879 read_profile(".profile");
12880 }
12881 state2:
12882 state = 3;
12883 if (
12884#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012885 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012886#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012887 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012888 ) {
12889 shinit = lookupvar("ENV");
12890 if (shinit != NULL && *shinit != '\0') {
12891 read_profile(shinit);
12892 }
12893 }
12894 state3:
12895 state = 4;
12896 if (minusc)
12897 evalstring(minusc, 0);
12898
12899 if (sflag || minusc == NULL) {
12900#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12901 if ( iflag ) {
12902 const char *hp = lookupvar("HISTFILE");
12903
12904 if (hp != NULL)
12905 line_input_state->hist_file = hp;
12906 }
12907#endif
12908 state4: /* XXX ??? - why isn't this before the "if" statement */
12909 cmdloop(1);
12910 }
12911#if PROFILE
12912 monitor(0);
12913#endif
12914#ifdef GPROF
12915 {
12916 extern void _mcleanup(void);
12917 _mcleanup();
12918 }
12919#endif
12920 exitshell();
12921 /* NOTREACHED */
12922}
12923
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012924#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012925const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012926int main(int argc, char **argv)
12927{
12928 return ash_main(argc, argv);
12929}
12930#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012931
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012932
Eric Andersendf82f612001-06-28 07:46:40 +000012933/*-
12934 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012935 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012936 *
12937 * This code is derived from software contributed to Berkeley by
12938 * Kenneth Almquist.
12939 *
12940 * Redistribution and use in source and binary forms, with or without
12941 * modification, are permitted provided that the following conditions
12942 * are met:
12943 * 1. Redistributions of source code must retain the above copyright
12944 * notice, this list of conditions and the following disclaimer.
12945 * 2. Redistributions in binary form must reproduce the above copyright
12946 * notice, this list of conditions and the following disclaimer in the
12947 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012948 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012949 * may be used to endorse or promote products derived from this software
12950 * without specific prior written permission.
12951 *
12952 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12953 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12954 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12955 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12956 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12957 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12958 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12959 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12960 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12961 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12962 * SUCH DAMAGE.
12963 */