blob: 16818cfc9c9d053ed7d8cebe7207cc045e62f7c5 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersen5bb16772001-09-06 18:00:41 +000045#define IFS_BROKEN
Eric Andersenc470f442003-07-28 09:56:35 +000046#define PROFILE 0
Denis Vlasenko131ae172007-02-18 13:00:19 +000047#if ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048#define JOBS 1
49#else
Denis Vlasenko666da5e2006-12-26 18:17:42 +000050#define JOBS 0
Eric Andersenc470f442003-07-28 09:56:35 +000051#endif
52
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#if DEBUG
54#define _GNU_SOURCE
55#endif
56#include "busybox.h"
57#include <paths.h>
58#include <setjmp.h>
59#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000060#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000061#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000062#endif
63
Denis Vlasenkob012b102007-02-19 22:43:01 +000064#if defined(__uClinux__)
65#error "Do not even bother, ash will not run on uClinux"
66#endif
67
Denis Vlasenkob012b102007-02-19 22:43:01 +000068
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000069/* ============ Misc helpers */
70
71#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
72
73/* C99 say: "char" declaration may be signed or unsigned default */
74#define signed_char2int(sc) ((int)((signed char)sc))
75
76
Denis Vlasenkob012b102007-02-19 22:43:01 +000077/* ============ Shell options */
78
79static const char *const optletters_optnames[] = {
80 "e" "errexit",
81 "f" "noglob",
82 "I" "ignoreeof",
83 "i" "interactive",
84 "m" "monitor",
85 "n" "noexec",
86 "s" "stdin",
87 "x" "xtrace",
88 "v" "verbose",
89 "C" "noclobber",
90 "a" "allexport",
91 "b" "notify",
92 "u" "nounset",
93 "\0" "vi",
94#if DEBUG
95 "\0" "nolog",
96 "\0" "debug",
97#endif
98};
99
100#define optletters(n) optletters_optnames[(n)][0]
101#define optnames(n) (&optletters_optnames[(n)][1])
102
103#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
104
105static char optlist[NOPTS];
106
107#define eflag optlist[0]
108#define fflag optlist[1]
109#define Iflag optlist[2]
110#define iflag optlist[3]
111#define mflag optlist[4]
112#define nflag optlist[5]
113#define sflag optlist[6]
114#define xflag optlist[7]
115#define vflag optlist[8]
116#define Cflag optlist[9]
117#define aflag optlist[10]
118#define bflag optlist[11]
119#define uflag optlist[12]
120#define viflag optlist[13]
121#if DEBUG
122#define nolog optlist[14]
123#define debug optlist[15]
124#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000125
126
Denis Vlasenkob012b102007-02-19 22:43:01 +0000127/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000128
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000129#ifdef __GLIBC__
130/* glibc sucks */
131static int *dash_errno;
132#undef errno
133#define errno (*dash_errno)
134#endif
135
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000136static char nullstr[1]; /* zero length string */
137static const char homestr[] = "HOME";
138static const char snlfmt[] = "%s\n";
139static const char illnum[] = "Illegal number: %s";
140
Denis Vlasenkocc571512007-02-23 21:10:35 +0000141static char *minusc; /* argument to -c option */
142
Denis Vlasenkoa624c112007-02-19 22:45:43 +0000143static int isloginsh;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000144/* pid of main shell */
145static int rootpid;
146/* shell level: 0 for the main shell, 1 for its children, and so on */
147static int shlvl;
148#define rootshell (!shlvl)
149/* trap handler commands */
150static char *trap[NSIG];
151/* current value of signal */
152static char sigmode[NSIG - 1];
153/* indicates specified signal received */
154static char gotsig[NSIG - 1];
155static char *arg0; /* value of $0 */
Eric Andersenc470f442003-07-28 09:56:35 +0000156
157
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000158/* ============ Interrupts / exceptions */
159
160/*
Eric Andersenc470f442003-07-28 09:56:35 +0000161 * We enclose jmp_buf in a structure so that we can declare pointers to
162 * jump locations. The global variable handler contains the location to
163 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000164 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000165 * exception handlers, the user should save the value of handler on entry
166 * to an inner scope, set handler to point to a jmploc structure for the
167 * inner scope, and restore handler on exit from the scope.
168 */
Eric Andersenc470f442003-07-28 09:56:35 +0000169struct jmploc {
170 jmp_buf loc;
171};
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000172static struct jmploc *exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +0000173static int exception;
Eric Andersenc470f442003-07-28 09:56:35 +0000174/* exceptions */
175#define EXINT 0 /* SIGINT received */
176#define EXERROR 1 /* a generic error */
177#define EXSHELLPROC 2 /* execute a shell procedure */
178#define EXEXEC 3 /* command execution failed */
179#define EXEXIT 4 /* exit the shell */
180#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000181static volatile int suppressint;
182static volatile sig_atomic_t intpending;
Eric Andersenc470f442003-07-28 09:56:35 +0000183/* do we generate EXSIG events */
184static int exsig;
185/* last pending signal */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000186static volatile sig_atomic_t pendingsig;
Eric Andersen2870d962001-07-02 17:27:21 +0000187
188/*
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000189 * Sigmode records the current value of the signal handlers for the various
190 * modes. A value of zero means that the current handler is not known.
191 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
192 */
193
194#define S_DFL 1 /* default signal handling (SIG_DFL) */
195#define S_CATCH 2 /* signal is caught */
196#define S_IGN 3 /* signal is ignored (SIG_IGN) */
197#define S_HARD_IGN 4 /* signal is ignored permenantly */
198#define S_RESET 5 /* temporary - to reset a hard ignored sig */
199
200/*
Eric Andersen2870d962001-07-02 17:27:21 +0000201 * These macros allow the user to suspend the handling of interrupt signals
202 * over a period of time. This is similar to SIGHOLD to or sigblock, but
203 * much more efficient and portable. (But hacking the kernel is so much
204 * more fun than worrying about efficiency and portability. :-))
205 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000206#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000207 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000208 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000209 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000210 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000211
212/*
213 * Called to raise an exception. Since C doesn't include exceptions, we
214 * just do a longjmp to the exception handler. The type of exception is
215 * stored in the global variable "exception".
216 */
217static void raise_exception(int) ATTRIBUTE_NORETURN;
218static void
219raise_exception(int e)
220{
221#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000222 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000223 abort();
224#endif
225 INT_OFF;
226 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000227 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000228}
229
230/*
231 * Called from trap.c when a SIGINT is received. (If the user specifies
232 * that SIGINT is to be trapped or ignored using the trap builtin, then
233 * this routine is not called.) Suppressint is nonzero when interrupts
234 * are held using the INT_OFF macro. (The test for iflag is just
235 * defensive programming.)
236 */
237static void raise_interrupt(void) ATTRIBUTE_NORETURN;
238static void
239raise_interrupt(void)
240{
241 int i;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000242 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000243
244 intpending = 0;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000245 /* Signal is not automatically re-enabled after it is raised,
246 * do it ourself */
247 sigemptyset(&mask);
248 sigprocmask(SIG_SETMASK, &mask, 0);
249 /* pendingsig = 0; - now done in onsig() */
250
Denis Vlasenkob012b102007-02-19 22:43:01 +0000251 i = EXSIG;
252 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
253 if (!(rootshell && iflag)) {
254 signal(SIGINT, SIG_DFL);
255 raise(SIGINT);
256 }
257 i = EXINT;
258 }
259 raise_exception(i);
260 /* NOTREACHED */
261}
262
263#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000264static void
265int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000266{
267 if (--suppressint == 0 && intpending) {
268 raise_interrupt();
269 }
270}
271#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000272static void
273force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000274{
275 suppressint = 0;
276 if (intpending)
277 raise_interrupt();
278}
279#define FORCE_INT_ON force_int_on()
280#else
281#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000282 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000283 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000284 if (--suppressint == 0 && intpending) \
285 raise_interrupt(); \
286 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000287#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000288 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000289 xbarrier(); \
290 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000291 if (intpending) \
292 raise_interrupt(); \
293 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000294#endif /* ASH_OPTIMIZE_FOR_SIZE */
295
296#define SAVE_INT(v) ((v) = suppressint)
297
298#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000299 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000300 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000301 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000302 if (suppressint == 0 && intpending) \
303 raise_interrupt(); \
304 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000305
306#define EXSIGON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000307 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000308 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000309 xbarrier(); \
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000310 if (pendingsig) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000311 raise_exception(EXSIG); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000312 } while (0)
Eric Andersenc470f442003-07-28 09:56:35 +0000313/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000314
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000315/*
316 * Ignore a signal. Only one usage site - in forkchild()
317 */
318static void
319ignoresig(int signo)
320{
321 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
322 signal(signo, SIG_IGN);
323 }
324 sigmode[signo - 1] = S_HARD_IGN;
325}
326
327/*
328 * Signal handler. Only one usage site - in setsignal()
329 */
330static void
331onsig(int signo)
332{
333 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000334 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000335
336 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000337 if (!suppressint) {
338 pendingsig = 0;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000339 raise_interrupt();
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000340 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000341 intpending = 1;
342 }
343}
344
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000345
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000346/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000347
Eric Andersenc470f442003-07-28 09:56:35 +0000348static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000349outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000350{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000351 INT_OFF;
352 fputs(p, file);
353 INT_ON;
354}
355
356static void
357flush_stdout_stderr(void)
358{
359 INT_OFF;
360 fflush(stdout);
361 fflush(stderr);
362 INT_ON;
363}
364
365static void
366flush_stderr(void)
367{
368 INT_OFF;
369 fflush(stderr);
370 INT_ON;
371}
372
373static void
374outcslow(int c, FILE *dest)
375{
376 INT_OFF;
377 putc(c, dest);
378 fflush(dest);
379 INT_ON;
380}
381
382static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
383static int
384out1fmt(const char *fmt, ...)
385{
386 va_list ap;
387 int r;
388
389 INT_OFF;
390 va_start(ap, fmt);
391 r = vprintf(fmt, ap);
392 va_end(ap);
393 INT_ON;
394 return r;
395}
396
397static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
398static int
399fmtstr(char *outbuf, size_t length, const char *fmt, ...)
400{
401 va_list ap;
402 int ret;
403
404 va_start(ap, fmt);
405 INT_OFF;
406 ret = vsnprintf(outbuf, length, fmt, ap);
407 va_end(ap);
408 INT_ON;
409 return ret;
410}
411
412static void
413out1str(const char *p)
414{
415 outstr(p, stdout);
416}
417
418static void
419out2str(const char *p)
420{
421 outstr(p, stderr);
422 flush_stderr();
423}
424
425
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000426/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000427
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000428/* control characters in argument strings */
429#define CTLESC '\201' /* escape next character */
430#define CTLVAR '\202' /* variable defn */
431#define CTLENDVAR '\203'
432#define CTLBACKQ '\204'
433#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
434/* CTLBACKQ | CTLQUOTE == '\205' */
435#define CTLARI '\206' /* arithmetic expression */
436#define CTLENDARI '\207'
437#define CTLQUOTEMARK '\210'
438
439/* variable substitution byte (follows CTLVAR) */
440#define VSTYPE 0x0f /* type of variable substitution */
441#define VSNUL 0x10 /* colon--treat the empty string as unset */
442#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
443
444/* values of VSTYPE field */
445#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
446#define VSMINUS 0x2 /* ${var-text} */
447#define VSPLUS 0x3 /* ${var+text} */
448#define VSQUESTION 0x4 /* ${var?message} */
449#define VSASSIGN 0x5 /* ${var=text} */
450#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
451#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
452#define VSTRIMLEFT 0x8 /* ${var#pattern} */
453#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
454#define VSLENGTH 0xa /* ${#var} */
455
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000456static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
457
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000458#define NCMD 0
459#define NPIPE 1
460#define NREDIR 2
461#define NBACKGND 3
462#define NSUBSHELL 4
463#define NAND 5
464#define NOR 6
465#define NSEMI 7
466#define NIF 8
467#define NWHILE 9
468#define NUNTIL 10
469#define NFOR 11
470#define NCASE 12
471#define NCLIST 13
472#define NDEFUN 14
473#define NARG 15
474#define NTO 16
475#define NCLOBBER 17
476#define NFROM 18
477#define NFROMTO 19
478#define NAPPEND 20
479#define NTOFD 21
480#define NFROMFD 22
481#define NHERE 23
482#define NXHERE 24
483#define NNOT 25
484
485union node;
486
487struct ncmd {
488 int type;
489 union node *assign;
490 union node *args;
491 union node *redirect;
492};
493
494struct npipe {
495 int type;
496 int backgnd;
497 struct nodelist *cmdlist;
498};
499
500struct nredir {
501 int type;
502 union node *n;
503 union node *redirect;
504};
505
506struct nbinary {
507 int type;
508 union node *ch1;
509 union node *ch2;
510};
511
512struct nif {
513 int type;
514 union node *test;
515 union node *ifpart;
516 union node *elsepart;
517};
518
519struct nfor {
520 int type;
521 union node *args;
522 union node *body;
523 char *var;
524};
525
526struct ncase {
527 int type;
528 union node *expr;
529 union node *cases;
530};
531
532struct nclist {
533 int type;
534 union node *next;
535 union node *pattern;
536 union node *body;
537};
538
539struct narg {
540 int type;
541 union node *next;
542 char *text;
543 struct nodelist *backquote;
544};
545
546struct nfile {
547 int type;
548 union node *next;
549 int fd;
550 union node *fname;
551 char *expfname;
552};
553
554struct ndup {
555 int type;
556 union node *next;
557 int fd;
558 int dupfd;
559 union node *vname;
560};
561
562struct nhere {
563 int type;
564 union node *next;
565 int fd;
566 union node *doc;
567};
568
569struct nnot {
570 int type;
571 union node *com;
572};
573
574union node {
575 int type;
576 struct ncmd ncmd;
577 struct npipe npipe;
578 struct nredir nredir;
579 struct nbinary nbinary;
580 struct nif nif;
581 struct nfor nfor;
582 struct ncase ncase;
583 struct nclist nclist;
584 struct narg narg;
585 struct nfile nfile;
586 struct ndup ndup;
587 struct nhere nhere;
588 struct nnot nnot;
589};
590
591struct nodelist {
592 struct nodelist *next;
593 union node *n;
594};
595
596struct funcnode {
597 int count;
598 union node n;
599};
600
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000601/*
602 * Free a parse tree.
603 */
604static void
605freefunc(struct funcnode *f)
606{
607 if (f && --f->count < 0)
608 free(f);
609}
610
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000611
612/* ============ Debugging output */
613
614#if DEBUG
615
616static FILE *tracefile;
617
618static void
619trace_printf(const char *fmt, ...)
620{
621 va_list va;
622
623 if (debug != 1)
624 return;
625 va_start(va, fmt);
626 vfprintf(tracefile, fmt, va);
627 va_end(va);
628}
629
630static void
631trace_vprintf(const char *fmt, va_list va)
632{
633 if (debug != 1)
634 return;
635 vfprintf(tracefile, fmt, va);
636}
637
638static void
639trace_puts(const char *s)
640{
641 if (debug != 1)
642 return;
643 fputs(s, tracefile);
644}
645
646static void
647trace_puts_quoted(char *s)
648{
649 char *p;
650 char c;
651
652 if (debug != 1)
653 return;
654 putc('"', tracefile);
655 for (p = s; *p; p++) {
656 switch (*p) {
657 case '\n': c = 'n'; goto backslash;
658 case '\t': c = 't'; goto backslash;
659 case '\r': c = 'r'; goto backslash;
660 case '"': c = '"'; goto backslash;
661 case '\\': c = '\\'; goto backslash;
662 case CTLESC: c = 'e'; goto backslash;
663 case CTLVAR: c = 'v'; goto backslash;
664 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
665 case CTLBACKQ: c = 'q'; goto backslash;
666 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
667 backslash:
668 putc('\\', tracefile);
669 putc(c, tracefile);
670 break;
671 default:
672 if (*p >= ' ' && *p <= '~')
673 putc(*p, tracefile);
674 else {
675 putc('\\', tracefile);
676 putc(*p >> 6 & 03, tracefile);
677 putc(*p >> 3 & 07, tracefile);
678 putc(*p & 07, tracefile);
679 }
680 break;
681 }
682 }
683 putc('"', tracefile);
684}
685
686static void
687trace_puts_args(char **ap)
688{
689 if (debug != 1)
690 return;
691 if (!*ap)
692 return;
693 while (1) {
694 trace_puts_quoted(*ap);
695 if (!*++ap) {
696 putc('\n', tracefile);
697 break;
698 }
699 putc(' ', tracefile);
700 }
701}
702
703static void
704opentrace(void)
705{
706 char s[100];
707#ifdef O_APPEND
708 int flags;
709#endif
710
711 if (debug != 1) {
712 if (tracefile)
713 fflush(tracefile);
714 /* leave open because libedit might be using it */
715 return;
716 }
717 strcpy(s, "./trace");
718 if (tracefile) {
719 if (!freopen(s, "a", tracefile)) {
720 fprintf(stderr, "Can't re-open %s\n", s);
721 debug = 0;
722 return;
723 }
724 } else {
725 tracefile = fopen(s, "a");
726 if (tracefile == NULL) {
727 fprintf(stderr, "Can't open %s\n", s);
728 debug = 0;
729 return;
730 }
731 }
732#ifdef O_APPEND
733 flags = fcntl(fileno(tracefile), F_GETFL, 0);
734 if (flags >= 0)
735 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
736#endif
737 setlinebuf(tracefile);
738 fputs("\nTracing started.\n", tracefile);
739}
740
741static void
742indent(int amount, char *pfx, FILE *fp)
743{
744 int i;
745
746 for (i = 0; i < amount; i++) {
747 if (pfx && i == amount - 1)
748 fputs(pfx, fp);
749 putc('\t', fp);
750 }
751}
752
753/* little circular references here... */
754static void shtree(union node *n, int ind, char *pfx, FILE *fp);
755
756static void
757sharg(union node *arg, FILE *fp)
758{
759 char *p;
760 struct nodelist *bqlist;
761 int subtype;
762
763 if (arg->type != NARG) {
764 out1fmt("<node type %d>\n", arg->type);
765 abort();
766 }
767 bqlist = arg->narg.backquote;
768 for (p = arg->narg.text; *p; p++) {
769 switch (*p) {
770 case CTLESC:
771 putc(*++p, fp);
772 break;
773 case CTLVAR:
774 putc('$', fp);
775 putc('{', fp);
776 subtype = *++p;
777 if (subtype == VSLENGTH)
778 putc('#', fp);
779
780 while (*p != '=')
781 putc(*p++, fp);
782
783 if (subtype & VSNUL)
784 putc(':', fp);
785
786 switch (subtype & VSTYPE) {
787 case VSNORMAL:
788 putc('}', fp);
789 break;
790 case VSMINUS:
791 putc('-', fp);
792 break;
793 case VSPLUS:
794 putc('+', fp);
795 break;
796 case VSQUESTION:
797 putc('?', fp);
798 break;
799 case VSASSIGN:
800 putc('=', fp);
801 break;
802 case VSTRIMLEFT:
803 putc('#', fp);
804 break;
805 case VSTRIMLEFTMAX:
806 putc('#', fp);
807 putc('#', fp);
808 break;
809 case VSTRIMRIGHT:
810 putc('%', fp);
811 break;
812 case VSTRIMRIGHTMAX:
813 putc('%', fp);
814 putc('%', fp);
815 break;
816 case VSLENGTH:
817 break;
818 default:
819 out1fmt("<subtype %d>", subtype);
820 }
821 break;
822 case CTLENDVAR:
823 putc('}', fp);
824 break;
825 case CTLBACKQ:
826 case CTLBACKQ|CTLQUOTE:
827 putc('$', fp);
828 putc('(', fp);
829 shtree(bqlist->n, -1, NULL, fp);
830 putc(')', fp);
831 break;
832 default:
833 putc(*p, fp);
834 break;
835 }
836 }
837}
838
839static void
840shcmd(union node *cmd, FILE *fp)
841{
842 union node *np;
843 int first;
844 const char *s;
845 int dftfd;
846
847 first = 1;
848 for (np = cmd->ncmd.args; np; np = np->narg.next) {
849 if (! first)
850 putchar(' ');
851 sharg(np, fp);
852 first = 0;
853 }
854 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
855 if (! first)
856 putchar(' ');
857 switch (np->nfile.type) {
858 case NTO: s = ">"; dftfd = 1; break;
859 case NCLOBBER: s = ">|"; dftfd = 1; break;
860 case NAPPEND: s = ">>"; dftfd = 1; break;
861 case NTOFD: s = ">&"; dftfd = 1; break;
862 case NFROM: s = "<"; dftfd = 0; break;
863 case NFROMFD: s = "<&"; dftfd = 0; break;
864 case NFROMTO: s = "<>"; dftfd = 0; break;
865 default: s = "*error*"; dftfd = 0; break;
866 }
867 if (np->nfile.fd != dftfd)
868 fprintf(fp, "%d", np->nfile.fd);
869 fputs(s, fp);
870 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
871 fprintf(fp, "%d", np->ndup.dupfd);
872 } else {
873 sharg(np->nfile.fname, fp);
874 }
875 first = 0;
876 }
877}
878
879static void
880shtree(union node *n, int ind, char *pfx, FILE *fp)
881{
882 struct nodelist *lp;
883 const char *s;
884
885 if (n == NULL)
886 return;
887
888 indent(ind, pfx, fp);
889 switch (n->type) {
890 case NSEMI:
891 s = "; ";
892 goto binop;
893 case NAND:
894 s = " && ";
895 goto binop;
896 case NOR:
897 s = " || ";
898 binop:
899 shtree(n->nbinary.ch1, ind, NULL, fp);
900 /* if (ind < 0) */
901 fputs(s, fp);
902 shtree(n->nbinary.ch2, ind, NULL, fp);
903 break;
904 case NCMD:
905 shcmd(n, fp);
906 if (ind >= 0)
907 putc('\n', fp);
908 break;
909 case NPIPE:
910 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
911 shcmd(lp->n, fp);
912 if (lp->next)
913 fputs(" | ", fp);
914 }
915 if (n->npipe.backgnd)
916 fputs(" &", fp);
917 if (ind >= 0)
918 putc('\n', fp);
919 break;
920 default:
921 fprintf(fp, "<node type %d>", n->type);
922 if (ind >= 0)
923 putc('\n', fp);
924 break;
925 }
926}
927
928static void
929showtree(union node *n)
930{
931 trace_puts("showtree called\n");
932 shtree(n, 1, NULL, stdout);
933}
934
935#define TRACE(param) trace_printf param
936#define TRACEV(param) trace_vprintf param
937
938#else
939
940#define TRACE(param)
941#define TRACEV(param)
942
943#endif /* DEBUG */
944
945
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000946/* ============ Parser data */
947
948/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000949 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
950 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000951struct strlist {
952 struct strlist *next;
953 char *text;
954};
955
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000956#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000957struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000958#endif
959
Denis Vlasenkob012b102007-02-19 22:43:01 +0000960struct strpush {
961 struct strpush *prev; /* preceding string on stack */
962 char *prevstring;
963 int prevnleft;
964#if ENABLE_ASH_ALIAS
965 struct alias *ap; /* if push was associated with an alias */
966#endif
967 char *string; /* remember the string since it may change */
968};
969
970struct parsefile {
971 struct parsefile *prev; /* preceding file on stack */
972 int linno; /* current line */
973 int fd; /* file descriptor (or -1 if string) */
974 int nleft; /* number of chars left in this line */
975 int lleft; /* number of chars left in this buffer */
976 char *nextc; /* next char in buffer */
977 char *buf; /* input buffer */
978 struct strpush *strpush; /* for pushing strings at this level */
979 struct strpush basestrpush; /* so pushing one is fast */
980};
981
982static struct parsefile basepf; /* top level input file */
983static struct parsefile *parsefile = &basepf; /* current input file */
984static int startlinno; /* line # where last token started */
985static char *commandname; /* currently executing command */
986static struct strlist *cmdenviron; /* environment for builtin command */
987static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000988
989
990/* ============ Message printing */
991
992static void
993ash_vmsg(const char *msg, va_list ap)
994{
995 fprintf(stderr, "%s: ", arg0);
996 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +0000997 if (strcmp(arg0, commandname))
998 fprintf(stderr, "%s: ", commandname);
999 if (!iflag || parsefile->fd)
1000 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001001 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001002 vfprintf(stderr, msg, ap);
1003 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001004}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001005
1006/*
1007 * Exverror is called to raise the error exception. If the second argument
1008 * is not NULL then error prints an error message using printf style
1009 * formatting. It then raises the error exception.
1010 */
1011static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1012static void
1013ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001014{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001015#if DEBUG
1016 if (msg) {
1017 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1018 TRACEV((msg, ap));
1019 TRACE(("\") pid=%d\n", getpid()));
1020 } else
1021 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1022 if (msg)
1023#endif
1024 ash_vmsg(msg, ap);
1025
1026 flush_stdout_stderr();
1027 raise_exception(cond);
1028 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001029}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001030
1031static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1032static void
1033ash_msg_and_raise_error(const char *msg, ...)
1034{
1035 va_list ap;
1036
1037 va_start(ap, msg);
1038 ash_vmsg_and_raise(EXERROR, msg, ap);
1039 /* NOTREACHED */
1040 va_end(ap);
1041}
1042
1043static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1044static void
1045ash_msg_and_raise(int cond, const char *msg, ...)
1046{
1047 va_list ap;
1048
1049 va_start(ap, msg);
1050 ash_vmsg_and_raise(cond, msg, ap);
1051 /* NOTREACHED */
1052 va_end(ap);
1053}
1054
1055/*
1056 * error/warning routines for external builtins
1057 */
1058static void
1059ash_msg(const char *fmt, ...)
1060{
1061 va_list ap;
1062
1063 va_start(ap, fmt);
1064 ash_vmsg(fmt, ap);
1065 va_end(ap);
1066}
1067
1068/*
1069 * Return a string describing an error. The returned string may be a
1070 * pointer to a static buffer that will be overwritten on the next call.
1071 * Action describes the operation that got the error.
1072 */
1073static const char *
1074errmsg(int e, const char *em)
1075{
1076 if (e == ENOENT || e == ENOTDIR) {
1077 return em;
1078 }
1079 return strerror(e);
1080}
1081
1082
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001083/* ============ Memory allocation */
1084
1085/*
1086 * It appears that grabstackstr() will barf with such alignments
1087 * because stalloc() will return a string allocated in a new stackblock.
1088 */
1089#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1090enum {
1091 /* Most machines require the value returned from malloc to be aligned
1092 * in some way. The following macro will get this right
1093 * on many machines. */
1094 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1095 /* Minimum size of a block */
1096 MINSIZE = SHELL_ALIGN(504),
1097};
1098
1099struct stack_block {
1100 struct stack_block *prev;
1101 char space[MINSIZE];
1102};
1103
1104struct stackmark {
1105 struct stack_block *stackp;
1106 char *stacknxt;
1107 size_t stacknleft;
1108 struct stackmark *marknext;
1109};
1110
1111static struct stack_block stackbase;
1112static struct stack_block *stackp = &stackbase;
1113static struct stackmark *markp;
1114static char *stacknxt = stackbase.space;
1115static size_t stacknleft = MINSIZE;
1116static char *sstrend = stackbase.space + MINSIZE;
1117static int herefd = -1;
1118
1119#define stackblock() ((void *)stacknxt)
1120#define stackblocksize() stacknleft
1121
1122static void *
1123ckrealloc(void * p, size_t nbytes)
1124{
1125 p = realloc(p, nbytes);
1126 if (!p)
1127 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1128 return p;
1129}
1130
1131static void *
1132ckmalloc(size_t nbytes)
1133{
1134 return ckrealloc(NULL, nbytes);
1135}
1136
1137/*
1138 * Make a copy of a string in safe storage.
1139 */
1140static char *
1141ckstrdup(const char *s)
1142{
1143 char *p = strdup(s);
1144 if (!p)
1145 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1146 return p;
1147}
1148
1149/*
1150 * Parse trees for commands are allocated in lifo order, so we use a stack
1151 * to make this more efficient, and also to avoid all sorts of exception
1152 * handling code to handle interrupts in the middle of a parse.
1153 *
1154 * The size 504 was chosen because the Ultrix malloc handles that size
1155 * well.
1156 */
1157static void *
1158stalloc(size_t nbytes)
1159{
1160 char *p;
1161 size_t aligned;
1162
1163 aligned = SHELL_ALIGN(nbytes);
1164 if (aligned > stacknleft) {
1165 size_t len;
1166 size_t blocksize;
1167 struct stack_block *sp;
1168
1169 blocksize = aligned;
1170 if (blocksize < MINSIZE)
1171 blocksize = MINSIZE;
1172 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1173 if (len < blocksize)
1174 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1175 INT_OFF;
1176 sp = ckmalloc(len);
1177 sp->prev = stackp;
1178 stacknxt = sp->space;
1179 stacknleft = blocksize;
1180 sstrend = stacknxt + blocksize;
1181 stackp = sp;
1182 INT_ON;
1183 }
1184 p = stacknxt;
1185 stacknxt += aligned;
1186 stacknleft -= aligned;
1187 return p;
1188}
1189
1190static void
1191stunalloc(void *p)
1192{
1193#if DEBUG
1194 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
1195 write(2, "stunalloc\n", 10);
1196 abort();
1197 }
1198#endif
1199 stacknleft += stacknxt - (char *)p;
1200 stacknxt = p;
1201}
1202
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001203/*
1204 * Like strdup but works with the ash stack.
1205 */
1206static char *
1207ststrdup(const char *p)
1208{
1209 size_t len = strlen(p) + 1;
1210 return memcpy(stalloc(len), p, len);
1211}
1212
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001213static void
1214setstackmark(struct stackmark *mark)
1215{
1216 mark->stackp = stackp;
1217 mark->stacknxt = stacknxt;
1218 mark->stacknleft = stacknleft;
1219 mark->marknext = markp;
1220 markp = mark;
1221}
1222
1223static void
1224popstackmark(struct stackmark *mark)
1225{
1226 struct stack_block *sp;
1227
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001228 if (!mark->stackp)
1229 return;
1230
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001231 INT_OFF;
1232 markp = mark->marknext;
1233 while (stackp != mark->stackp) {
1234 sp = stackp;
1235 stackp = sp->prev;
1236 free(sp);
1237 }
1238 stacknxt = mark->stacknxt;
1239 stacknleft = mark->stacknleft;
1240 sstrend = mark->stacknxt + mark->stacknleft;
1241 INT_ON;
1242}
1243
1244/*
1245 * When the parser reads in a string, it wants to stick the string on the
1246 * stack and only adjust the stack pointer when it knows how big the
1247 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1248 * of space on top of the stack and stackblocklen returns the length of
1249 * this block. Growstackblock will grow this space by at least one byte,
1250 * possibly moving it (like realloc). Grabstackblock actually allocates the
1251 * part of the block that has been used.
1252 */
1253static void
1254growstackblock(void)
1255{
1256 size_t newlen;
1257
1258 newlen = stacknleft * 2;
1259 if (newlen < stacknleft)
1260 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1261 if (newlen < 128)
1262 newlen += 128;
1263
1264 if (stacknxt == stackp->space && stackp != &stackbase) {
1265 struct stack_block *oldstackp;
1266 struct stackmark *xmark;
1267 struct stack_block *sp;
1268 struct stack_block *prevstackp;
1269 size_t grosslen;
1270
1271 INT_OFF;
1272 oldstackp = stackp;
1273 sp = stackp;
1274 prevstackp = sp->prev;
1275 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1276 sp = ckrealloc(sp, grosslen);
1277 sp->prev = prevstackp;
1278 stackp = sp;
1279 stacknxt = sp->space;
1280 stacknleft = newlen;
1281 sstrend = sp->space + newlen;
1282
1283 /*
1284 * Stack marks pointing to the start of the old block
1285 * must be relocated to point to the new block
1286 */
1287 xmark = markp;
1288 while (xmark != NULL && xmark->stackp == oldstackp) {
1289 xmark->stackp = stackp;
1290 xmark->stacknxt = stacknxt;
1291 xmark->stacknleft = stacknleft;
1292 xmark = xmark->marknext;
1293 }
1294 INT_ON;
1295 } else {
1296 char *oldspace = stacknxt;
1297 int oldlen = stacknleft;
1298 char *p = stalloc(newlen);
1299
1300 /* free the space we just allocated */
1301 stacknxt = memcpy(p, oldspace, oldlen);
1302 stacknleft += newlen;
1303 }
1304}
1305
1306static void
1307grabstackblock(size_t len)
1308{
1309 len = SHELL_ALIGN(len);
1310 stacknxt += len;
1311 stacknleft -= len;
1312}
1313
1314/*
1315 * The following routines are somewhat easier to use than the above.
1316 * The user declares a variable of type STACKSTR, which may be declared
1317 * to be a register. The macro STARTSTACKSTR initializes things. Then
1318 * the user uses the macro STPUTC to add characters to the string. In
1319 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1320 * grown as necessary. When the user is done, she can just leave the
1321 * string there and refer to it using stackblock(). Or she can allocate
1322 * the space for it using grabstackstr(). If it is necessary to allow
1323 * someone else to use the stack temporarily and then continue to grow
1324 * the string, the user should use grabstack to allocate the space, and
1325 * then call ungrabstr(p) to return to the previous mode of operation.
1326 *
1327 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1328 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1329 * is space for at least one character.
1330 */
1331static void *
1332growstackstr(void)
1333{
1334 size_t len = stackblocksize();
1335 if (herefd >= 0 && len >= 1024) {
1336 full_write(herefd, stackblock(), len);
1337 return stackblock();
1338 }
1339 growstackblock();
1340 return stackblock() + len;
1341}
1342
1343/*
1344 * Called from CHECKSTRSPACE.
1345 */
1346static char *
1347makestrspace(size_t newlen, char *p)
1348{
1349 size_t len = p - stacknxt;
1350 size_t size = stackblocksize();
1351
1352 for (;;) {
1353 size_t nleft;
1354
1355 size = stackblocksize();
1356 nleft = size - len;
1357 if (nleft >= newlen)
1358 break;
1359 growstackblock();
1360 }
1361 return stackblock() + len;
1362}
1363
1364static char *
1365stack_nputstr(const char *s, size_t n, char *p)
1366{
1367 p = makestrspace(n, p);
1368 p = memcpy(p, s, n) + n;
1369 return p;
1370}
1371
1372static char *
1373stack_putstr(const char *s, char *p)
1374{
1375 return stack_nputstr(s, strlen(s), p);
1376}
1377
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001378static char *
1379_STPUTC(int c, char *p)
1380{
1381 if (p == sstrend)
1382 p = growstackstr();
1383 *p++ = c;
1384 return p;
1385}
1386
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001387#define STARTSTACKSTR(p) ((p) = stackblock())
1388#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001389#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001390 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001391 char *q = (p); \
1392 size_t l = (n); \
1393 size_t m = sstrend - q; \
1394 if (l > m) \
1395 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001396 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001397#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001398#define STACKSTRNUL(p) \
1399 do { \
1400 if ((p) == sstrend) \
1401 p = growstackstr(); \
1402 *p = '\0'; \
1403 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001404#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001405#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001406#define STADJUST(amount, p) (p += (amount))
1407
1408#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1409#define ungrabstackstr(s, p) stunalloc((s))
1410#define stackstrend() ((void *)sstrend)
1411
1412
1413/* ============ String helpers */
1414
1415/*
1416 * prefix -- see if pfx is a prefix of string.
1417 */
1418static char *
1419prefix(const char *string, const char *pfx)
1420{
1421 while (*pfx) {
1422 if (*pfx++ != *string++)
1423 return 0;
1424 }
1425 return (char *) string;
1426}
1427
1428/*
1429 * Check for a valid number. This should be elsewhere.
1430 */
1431static int
1432is_number(const char *p)
1433{
1434 do {
1435 if (!isdigit(*p))
1436 return 0;
1437 } while (*++p != '\0');
1438 return 1;
1439}
1440
1441/*
1442 * Convert a string of digits to an integer, printing an error message on
1443 * failure.
1444 */
1445static int
1446number(const char *s)
1447{
1448 if (!is_number(s))
1449 ash_msg_and_raise_error(illnum, s);
1450 return atoi(s);
1451}
1452
1453/*
1454 * Produce a possibly single quoted string suitable as input to the shell.
1455 * The return string is allocated on the stack.
1456 */
1457static char *
1458single_quote(const char *s)
1459{
1460 char *p;
1461
1462 STARTSTACKSTR(p);
1463
1464 do {
1465 char *q;
1466 size_t len;
1467
1468 len = strchrnul(s, '\'') - s;
1469
1470 q = p = makestrspace(len + 3, p);
1471
1472 *q++ = '\'';
1473 q = memcpy(q, s, len) + len;
1474 *q++ = '\'';
1475 s += len;
1476
1477 STADJUST(q - p, p);
1478
1479 len = strspn(s, "'");
1480 if (!len)
1481 break;
1482
1483 q = p = makestrspace(len + 3, p);
1484
1485 *q++ = '"';
1486 q = memcpy(q, s, len) + len;
1487 *q++ = '"';
1488 s += len;
1489
1490 STADJUST(q - p, p);
1491 } while (*s);
1492
1493 USTPUTC(0, p);
1494
1495 return stackblock();
1496}
1497
1498
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001499/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001500
1501static char **argptr; /* argument list for builtin commands */
1502static char *optionarg; /* set by nextopt (like getopt) */
1503static char *optptr; /* used by nextopt */
1504
1505/*
1506 * XXX - should get rid of. have all builtins use getopt(3). the
1507 * library getopt must have the BSD extension static variable "optreset"
1508 * otherwise it can't be used within the shell safely.
1509 *
1510 * Standard option processing (a la getopt) for builtin routines. The
1511 * only argument that is passed to nextopt is the option string; the
1512 * other arguments are unnecessary. It return the character, or '\0' on
1513 * end of input.
1514 */
1515static int
1516nextopt(const char *optstring)
1517{
1518 char *p;
1519 const char *q;
1520 char c;
1521
1522 p = optptr;
1523 if (p == NULL || *p == '\0') {
1524 p = *argptr;
1525 if (p == NULL || *p != '-' || *++p == '\0')
1526 return '\0';
1527 argptr++;
1528 if (LONE_DASH(p)) /* check for "--" */
1529 return '\0';
1530 }
1531 c = *p++;
1532 for (q = optstring; *q != c; ) {
1533 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001534 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001535 if (*++q == ':')
1536 q++;
1537 }
1538 if (*++q == ':') {
1539 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001540 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001541 optionarg = p;
1542 p = NULL;
1543 }
1544 optptr = p;
1545 return c;
1546}
1547
1548
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001549/* ============ Math support definitions */
1550
1551#if ENABLE_ASH_MATH_SUPPORT_64
1552typedef int64_t arith_t;
1553#define arith_t_type long long
1554#else
1555typedef long arith_t;
1556#define arith_t_type long
1557#endif
1558
1559#if ENABLE_ASH_MATH_SUPPORT
1560static arith_t dash_arith(const char *);
1561static arith_t arith(const char *expr, int *perrcode);
1562#endif
1563
1564#if ENABLE_ASH_RANDOM_SUPPORT
1565static unsigned long rseed;
1566#ifndef DYNAMIC_VAR
1567#define DYNAMIC_VAR
1568#endif
1569#endif
1570
1571
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001572/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001573
1574/* flags */
1575#define VEXPORT 0x01 /* variable is exported */
1576#define VREADONLY 0x02 /* variable cannot be modified */
1577#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1578#define VTEXTFIXED 0x08 /* text is statically allocated */
1579#define VSTACK 0x10 /* text is allocated on the stack */
1580#define VUNSET 0x20 /* the variable is not set */
1581#define VNOFUNC 0x40 /* don't call the callback function */
1582#define VNOSET 0x80 /* do not set variable - just readonly test */
1583#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1584#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001585# define VDYNAMIC 0x200 /* dynamic variable */
1586#else
1587# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001588#endif
1589
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001590static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1591#ifdef IFS_BROKEN
1592static const char defifsvar[] = "IFS= \t\n";
1593#define defifs (defifsvar + 4)
1594#else
1595static const char defifs[] = " \t\n";
1596#endif
1597
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001598struct shparam {
1599 int nparam; /* # of positional parameters (without $0) */
1600 unsigned char malloc; /* if parameter list dynamically allocated */
1601 char **p; /* parameter list */
1602#if ENABLE_ASH_GETOPTS
1603 int optind; /* next parameter to be processed by getopts */
1604 int optoff; /* used by getopts */
1605#endif
1606};
1607
1608static struct shparam shellparam; /* $@ current positional parameters */
1609
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00001610/*
1611 * Free the list of positional parameters.
1612 */
1613static void
1614freeparam(volatile struct shparam *param)
1615{
1616 char **ap;
1617
1618 if (param->malloc) {
1619 for (ap = param->p; *ap; ap++)
1620 free(*ap);
1621 free(param->p);
1622 }
1623}
1624
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001625#if ENABLE_ASH_GETOPTS
1626static void
1627getoptsreset(const char *value)
1628{
1629 shellparam.optind = number(value);
1630 shellparam.optoff = -1;
1631}
1632#endif
1633
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001634struct var {
1635 struct var *next; /* next entry in hash list */
1636 int flags; /* flags are defined above */
1637 const char *text; /* name=value */
1638 void (*func)(const char *); /* function to be called when */
1639 /* the variable gets set/unset */
1640};
1641
1642struct localvar {
1643 struct localvar *next; /* next local variable in list */
1644 struct var *vp; /* the variable that was made local */
1645 int flags; /* saved flags */
1646 const char *text; /* saved text */
1647};
1648
1649/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001650#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001651static void
1652change_lc_all(const char *value)
1653{
1654 if (value && *value != '\0')
1655 setlocale(LC_ALL, value);
1656}
1657static void
1658change_lc_ctype(const char *value)
1659{
1660 if (value && *value != '\0')
1661 setlocale(LC_CTYPE, value);
1662}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001663#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001664#if ENABLE_ASH_MAIL
1665static void chkmail(void);
1666static void changemail(const char *);
1667#endif
1668static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001669#if ENABLE_ASH_RANDOM_SUPPORT
1670static void change_random(const char *);
1671#endif
1672
1673static struct var varinit[] = {
1674#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001675 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001676#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001677 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001678#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001679#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001680 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1681 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001682#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001683 { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1684 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1685 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1686 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001687#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001688 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001689#endif
1690#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001691 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001692#endif
1693#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001694 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1695 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001696#endif
1697#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001698 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001699#endif
1700};
1701
1702#define vifs varinit[0]
1703#if ENABLE_ASH_MAIL
1704#define vmail (&vifs)[1]
1705#define vmpath (&vmail)[1]
1706#else
1707#define vmpath vifs
1708#endif
1709#define vpath (&vmpath)[1]
1710#define vps1 (&vpath)[1]
1711#define vps2 (&vps1)[1]
1712#define vps4 (&vps2)[1]
1713#define voptind (&vps4)[1]
1714#if ENABLE_ASH_GETOPTS
1715#define vrandom (&voptind)[1]
1716#else
1717#define vrandom (&vps4)[1]
1718#endif
1719#define defpath (defpathvar + 5)
1720
1721/*
1722 * The following macros access the values of the above variables.
1723 * They have to skip over the name. They return the null string
1724 * for unset variables.
1725 */
1726#define ifsval() (vifs.text + 4)
1727#define ifsset() ((vifs.flags & VUNSET) == 0)
1728#define mailval() (vmail.text + 5)
1729#define mpathval() (vmpath.text + 9)
1730#define pathval() (vpath.text + 5)
1731#define ps1val() (vps1.text + 4)
1732#define ps2val() (vps2.text + 4)
1733#define ps4val() (vps4.text + 4)
1734#define optindval() (voptind.text + 7)
1735
1736#define mpathset() ((vmpath.flags & VUNSET) == 0)
1737
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738/*
1739 * The parsefile structure pointed to by the global variable parsefile
1740 * contains information about the current file being read.
1741 */
1742struct redirtab {
1743 struct redirtab *next;
1744 int renamed[10];
1745 int nullredirs;
1746};
1747
1748static struct redirtab *redirlist;
1749static int nullredirs;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001750extern char **environ;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001751static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1752
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001753#define VTABSIZE 39
1754
1755static struct var *vartab[VTABSIZE];
1756
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1758#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1759
1760/*
1761 * Return of a legal variable name (a letter or underscore followed by zero or
1762 * more letters, underscores, and digits).
1763 */
1764static char *
1765endofname(const char *name)
1766{
1767 char *p;
1768
1769 p = (char *) name;
1770 if (!is_name(*p))
1771 return p;
1772 while (*++p) {
1773 if (!is_in_name(*p))
1774 break;
1775 }
1776 return p;
1777}
1778
1779/*
1780 * Compares two strings up to the first = or '\0'. The first
1781 * string must be terminated by '='; the second may be terminated by
1782 * either '=' or '\0'.
1783 */
1784static int
1785varcmp(const char *p, const char *q)
1786{
1787 int c, d;
1788
1789 while ((c = *p) == (d = *q)) {
1790 if (!c || c == '=')
1791 goto out;
1792 p++;
1793 q++;
1794 }
1795 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001796 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001797 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001798 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001799 out:
1800 return c - d;
1801}
1802
1803static int
1804varequal(const char *a, const char *b)
1805{
1806 return !varcmp(a, b);
1807}
1808
1809/*
1810 * Find the appropriate entry in the hash table from the name.
1811 */
1812static struct var **
1813hashvar(const char *p)
1814{
1815 unsigned hashval;
1816
1817 hashval = ((unsigned char) *p) << 4;
1818 while (*p && *p != '=')
1819 hashval += (unsigned char) *p++;
1820 return &vartab[hashval % VTABSIZE];
1821}
1822
1823static int
1824vpcmp(const void *a, const void *b)
1825{
1826 return varcmp(*(const char **)a, *(const char **)b);
1827}
1828
1829/*
1830 * This routine initializes the builtin variables.
1831 */
1832static void
1833initvar(void)
1834{
1835 struct var *vp;
1836 struct var *end;
1837 struct var **vpp;
1838
1839 /*
1840 * PS1 depends on uid
1841 */
1842#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1843 vps1.text = "PS1=\\w \\$ ";
1844#else
1845 if (!geteuid())
1846 vps1.text = "PS1=# ";
1847#endif
1848 vp = varinit;
1849 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1850 do {
1851 vpp = hashvar(vp->text);
1852 vp->next = *vpp;
1853 *vpp = vp;
1854 } while (++vp < end);
1855}
1856
1857static struct var **
1858findvar(struct var **vpp, const char *name)
1859{
1860 for (; *vpp; vpp = &(*vpp)->next) {
1861 if (varequal((*vpp)->text, name)) {
1862 break;
1863 }
1864 }
1865 return vpp;
1866}
1867
1868/*
1869 * Find the value of a variable. Returns NULL if not set.
1870 */
1871static char *
1872lookupvar(const char *name)
1873{
1874 struct var *v;
1875
1876 v = *findvar(hashvar(name), name);
1877 if (v) {
1878#ifdef DYNAMIC_VAR
1879 /*
1880 * Dynamic variables are implemented roughly the same way they are
1881 * in bash. Namely, they're "special" so long as they aren't unset.
1882 * As soon as they're unset, they're no longer dynamic, and dynamic
1883 * lookup will no longer happen at that point. -- PFM.
1884 */
1885 if ((v->flags & VDYNAMIC))
1886 (*v->func)(NULL);
1887#endif
1888 if (!(v->flags & VUNSET))
1889 return strchrnul(v->text, '=') + 1;
1890 }
1891 return NULL;
1892}
1893
1894/*
1895 * Search the environment of a builtin command.
1896 */
1897static char *
1898bltinlookup(const char *name)
1899{
1900 struct strlist *sp;
1901
1902 for (sp = cmdenviron; sp; sp = sp->next) {
1903 if (varequal(sp->text, name))
1904 return strchrnul(sp->text, '=') + 1;
1905 }
1906 return lookupvar(name);
1907}
1908
1909/*
1910 * Same as setvar except that the variable and value are passed in
1911 * the first argument as name=value. Since the first argument will
1912 * be actually stored in the table, it should not be a string that
1913 * will go away.
1914 * Called with interrupts off.
1915 */
1916static void
1917setvareq(char *s, int flags)
1918{
1919 struct var *vp, **vpp;
1920
1921 vpp = hashvar(s);
1922 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1923 vp = *findvar(vpp, s);
1924 if (vp) {
1925 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1926 const char *n;
1927
1928 if (flags & VNOSAVE)
1929 free(s);
1930 n = vp->text;
1931 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1932 }
1933
1934 if (flags & VNOSET)
1935 return;
1936
1937 if (vp->func && (flags & VNOFUNC) == 0)
1938 (*vp->func)(strchrnul(s, '=') + 1);
1939
1940 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1941 free((char*)vp->text);
1942
1943 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1944 } else {
1945 if (flags & VNOSET)
1946 return;
1947 /* not found */
1948 vp = ckmalloc(sizeof(*vp));
1949 vp->next = *vpp;
1950 vp->func = NULL;
1951 *vpp = vp;
1952 }
1953 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1954 s = ckstrdup(s);
1955 vp->text = s;
1956 vp->flags = flags;
1957}
1958
1959/*
1960 * Set the value of a variable. The flags argument is ored with the
1961 * flags of the variable. If val is NULL, the variable is unset.
1962 */
1963static void
1964setvar(const char *name, const char *val, int flags)
1965{
1966 char *p, *q;
1967 size_t namelen;
1968 char *nameeq;
1969 size_t vallen;
1970
1971 q = endofname(name);
1972 p = strchrnul(q, '=');
1973 namelen = p - name;
1974 if (!namelen || p != q)
1975 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1976 vallen = 0;
1977 if (val == NULL) {
1978 flags |= VUNSET;
1979 } else {
1980 vallen = strlen(val);
1981 }
1982 INT_OFF;
1983 nameeq = ckmalloc(namelen + vallen + 2);
1984 p = memcpy(nameeq, name, namelen) + namelen;
1985 if (val) {
1986 *p++ = '=';
1987 p = memcpy(p, val, vallen) + vallen;
1988 }
1989 *p = '\0';
1990 setvareq(nameeq, flags | VNOSAVE);
1991 INT_ON;
1992}
1993
1994#if ENABLE_ASH_GETOPTS
1995/*
1996 * Safe version of setvar, returns 1 on success 0 on failure.
1997 */
1998static int
1999setvarsafe(const char *name, const char *val, int flags)
2000{
2001 int err;
2002 volatile int saveint;
2003 struct jmploc *volatile savehandler = exception_handler;
2004 struct jmploc jmploc;
2005
2006 SAVE_INT(saveint);
2007 if (setjmp(jmploc.loc))
2008 err = 1;
2009 else {
2010 exception_handler = &jmploc;
2011 setvar(name, val, flags);
2012 err = 0;
2013 }
2014 exception_handler = savehandler;
2015 RESTORE_INT(saveint);
2016 return err;
2017}
2018#endif
2019
2020/*
2021 * Unset the specified variable.
2022 */
2023static int
2024unsetvar(const char *s)
2025{
2026 struct var **vpp;
2027 struct var *vp;
2028 int retval;
2029
2030 vpp = findvar(hashvar(s), s);
2031 vp = *vpp;
2032 retval = 2;
2033 if (vp) {
2034 int flags = vp->flags;
2035
2036 retval = 1;
2037 if (flags & VREADONLY)
2038 goto out;
2039#ifdef DYNAMIC_VAR
2040 vp->flags &= ~VDYNAMIC;
2041#endif
2042 if (flags & VUNSET)
2043 goto ok;
2044 if ((flags & VSTRFIXED) == 0) {
2045 INT_OFF;
2046 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2047 free((char*)vp->text);
2048 *vpp = vp->next;
2049 free(vp);
2050 INT_ON;
2051 } else {
2052 setvar(s, 0, 0);
2053 vp->flags &= ~VEXPORT;
2054 }
2055 ok:
2056 retval = 0;
2057 }
2058 out:
2059 return retval;
2060}
2061
2062/*
2063 * Process a linked list of variable assignments.
2064 */
2065static void
2066listsetvar(struct strlist *list_set_var, int flags)
2067{
2068 struct strlist *lp = list_set_var;
2069
2070 if (!lp)
2071 return;
2072 INT_OFF;
2073 do {
2074 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002075 lp = lp->next;
2076 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002077 INT_ON;
2078}
2079
2080/*
2081 * Generate a list of variables satisfying the given conditions.
2082 */
2083static char **
2084listvars(int on, int off, char ***end)
2085{
2086 struct var **vpp;
2087 struct var *vp;
2088 char **ep;
2089 int mask;
2090
2091 STARTSTACKSTR(ep);
2092 vpp = vartab;
2093 mask = on | off;
2094 do {
2095 for (vp = *vpp; vp; vp = vp->next) {
2096 if ((vp->flags & mask) == on) {
2097 if (ep == stackstrend())
2098 ep = growstackstr();
2099 *ep++ = (char *) vp->text;
2100 }
2101 }
2102 } while (++vpp < vartab + VTABSIZE);
2103 if (ep == stackstrend())
2104 ep = growstackstr();
2105 if (end)
2106 *end = ep;
2107 *ep++ = NULL;
2108 return grabstackstr(ep);
2109}
2110
2111
2112/* ============ Path search helper
2113 *
2114 * The variable path (passed by reference) should be set to the start
2115 * of the path before the first call; padvance will update
2116 * this value as it proceeds. Successive calls to padvance will return
2117 * the possible path expansions in sequence. If an option (indicated by
2118 * a percent sign) appears in the path entry then the global variable
2119 * pathopt will be set to point to it; otherwise pathopt will be set to
2120 * NULL.
2121 */
2122static const char *pathopt; /* set by padvance */
2123
2124static char *
2125padvance(const char **path, const char *name)
2126{
2127 const char *p;
2128 char *q;
2129 const char *start;
2130 size_t len;
2131
2132 if (*path == NULL)
2133 return NULL;
2134 start = *path;
2135 for (p = start; *p && *p != ':' && *p != '%'; p++);
2136 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2137 while (stackblocksize() < len)
2138 growstackblock();
2139 q = stackblock();
2140 if (p != start) {
2141 memcpy(q, start, p - start);
2142 q += p - start;
2143 *q++ = '/';
2144 }
2145 strcpy(q, name);
2146 pathopt = NULL;
2147 if (*p == '%') {
2148 pathopt = ++p;
2149 while (*p && *p != ':') p++;
2150 }
2151 if (*p == ':')
2152 *path = p + 1;
2153 else
2154 *path = NULL;
2155 return stalloc(len);
2156}
2157
2158
2159/* ============ Prompt */
2160
2161static int doprompt; /* if set, prompt the user */
2162static int needprompt; /* true if interactive and at start of line */
2163
2164#if ENABLE_FEATURE_EDITING
2165static line_input_t *line_input_state;
2166static const char *cmdedit_prompt;
2167static void
2168putprompt(const char *s)
2169{
2170 if (ENABLE_ASH_EXPAND_PRMT) {
2171 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002172 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002173 return;
2174 }
2175 cmdedit_prompt = s;
2176}
2177#else
2178static void
2179putprompt(const char *s)
2180{
2181 out2str(s);
2182}
2183#endif
2184
2185#if ENABLE_ASH_EXPAND_PRMT
2186/* expandstr() needs parsing machinery, so it is far away ahead... */
2187static const char *expandstr(const char *ps);
2188#else
2189#define expandstr(s) s
2190#endif
2191
2192static void
2193setprompt(int whichprompt)
2194{
2195 const char *prompt;
2196#if ENABLE_ASH_EXPAND_PRMT
2197 struct stackmark smark;
2198#endif
2199
2200 needprompt = 0;
2201
2202 switch (whichprompt) {
2203 case 1:
2204 prompt = ps1val();
2205 break;
2206 case 2:
2207 prompt = ps2val();
2208 break;
2209 default: /* 0 */
2210 prompt = nullstr;
2211 }
2212#if ENABLE_ASH_EXPAND_PRMT
2213 setstackmark(&smark);
2214 stalloc(stackblocksize());
2215#endif
2216 putprompt(expandstr(prompt));
2217#if ENABLE_ASH_EXPAND_PRMT
2218 popstackmark(&smark);
2219#endif
2220}
2221
2222
2223/* ============ The cd and pwd commands */
2224
2225#define CD_PHYSICAL 1
2226#define CD_PRINT 2
2227
2228static int docd(const char *, int);
2229
2230static char *curdir = nullstr; /* current working directory */
2231static char *physdir = nullstr; /* physical working directory */
2232
2233static int
2234cdopt(void)
2235{
2236 int flags = 0;
2237 int i, j;
2238
2239 j = 'L';
2240 while ((i = nextopt("LP"))) {
2241 if (i != j) {
2242 flags ^= CD_PHYSICAL;
2243 j = i;
2244 }
2245 }
2246
2247 return flags;
2248}
2249
2250/*
2251 * Update curdir (the name of the current directory) in response to a
2252 * cd command.
2253 */
2254static const char *
2255updatepwd(const char *dir)
2256{
2257 char *new;
2258 char *p;
2259 char *cdcomppath;
2260 const char *lim;
2261
2262 cdcomppath = ststrdup(dir);
2263 STARTSTACKSTR(new);
2264 if (*dir != '/') {
2265 if (curdir == nullstr)
2266 return 0;
2267 new = stack_putstr(curdir, new);
2268 }
2269 new = makestrspace(strlen(dir) + 2, new);
2270 lim = stackblock() + 1;
2271 if (*dir != '/') {
2272 if (new[-1] != '/')
2273 USTPUTC('/', new);
2274 if (new > lim && *lim == '/')
2275 lim++;
2276 } else {
2277 USTPUTC('/', new);
2278 cdcomppath++;
2279 if (dir[1] == '/' && dir[2] != '/') {
2280 USTPUTC('/', new);
2281 cdcomppath++;
2282 lim++;
2283 }
2284 }
2285 p = strtok(cdcomppath, "/");
2286 while (p) {
2287 switch (*p) {
2288 case '.':
2289 if (p[1] == '.' && p[2] == '\0') {
2290 while (new > lim) {
2291 STUNPUTC(new);
2292 if (new[-1] == '/')
2293 break;
2294 }
2295 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002296 }
2297 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002298 break;
2299 /* fall through */
2300 default:
2301 new = stack_putstr(p, new);
2302 USTPUTC('/', new);
2303 }
2304 p = strtok(0, "/");
2305 }
2306 if (new > lim)
2307 STUNPUTC(new);
2308 *new = 0;
2309 return stackblock();
2310}
2311
2312/*
2313 * Find out what the current directory is. If we already know the current
2314 * directory, this routine returns immediately.
2315 */
2316static char *
2317getpwd(void)
2318{
2319 char *dir = getcwd(0, 0);
2320 return dir ? dir : nullstr;
2321}
2322
2323static void
2324setpwd(const char *val, int setold)
2325{
2326 char *oldcur, *dir;
2327
2328 oldcur = dir = curdir;
2329
2330 if (setold) {
2331 setvar("OLDPWD", oldcur, VEXPORT);
2332 }
2333 INT_OFF;
2334 if (physdir != nullstr) {
2335 if (physdir != oldcur)
2336 free(physdir);
2337 physdir = nullstr;
2338 }
2339 if (oldcur == val || !val) {
2340 char *s = getpwd();
2341 physdir = s;
2342 if (!val)
2343 dir = s;
2344 } else
2345 dir = ckstrdup(val);
2346 if (oldcur != dir && oldcur != nullstr) {
2347 free(oldcur);
2348 }
2349 curdir = dir;
2350 INT_ON;
2351 setvar("PWD", dir, VEXPORT);
2352}
2353
2354static void hashcd(void);
2355
2356/*
2357 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2358 * know that the current directory has changed.
2359 */
2360static int
2361docd(const char *dest, int flags)
2362{
2363 const char *dir = 0;
2364 int err;
2365
2366 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2367
2368 INT_OFF;
2369 if (!(flags & CD_PHYSICAL)) {
2370 dir = updatepwd(dest);
2371 if (dir)
2372 dest = dir;
2373 }
2374 err = chdir(dest);
2375 if (err)
2376 goto out;
2377 setpwd(dir, 1);
2378 hashcd();
2379 out:
2380 INT_ON;
2381 return err;
2382}
2383
2384static int
2385cdcmd(int argc, char **argv)
2386{
2387 const char *dest;
2388 const char *path;
2389 const char *p;
2390 char c;
2391 struct stat statb;
2392 int flags;
2393
2394 flags = cdopt();
2395 dest = *argptr;
2396 if (!dest)
2397 dest = bltinlookup(homestr);
2398 else if (LONE_DASH(dest)) {
2399 dest = bltinlookup("OLDPWD");
2400 flags |= CD_PRINT;
2401 }
2402 if (!dest)
2403 dest = nullstr;
2404 if (*dest == '/')
2405 goto step7;
2406 if (*dest == '.') {
2407 c = dest[1];
2408 dotdot:
2409 switch (c) {
2410 case '\0':
2411 case '/':
2412 goto step6;
2413 case '.':
2414 c = dest[2];
2415 if (c != '.')
2416 goto dotdot;
2417 }
2418 }
2419 if (!*dest)
2420 dest = ".";
2421 path = bltinlookup("CDPATH");
2422 if (!path) {
2423 step6:
2424 step7:
2425 p = dest;
2426 goto docd;
2427 }
2428 do {
2429 c = *path;
2430 p = padvance(&path, dest);
2431 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2432 if (c && c != ':')
2433 flags |= CD_PRINT;
2434 docd:
2435 if (!docd(p, flags))
2436 goto out;
2437 break;
2438 }
2439 } while (path);
2440 ash_msg_and_raise_error("can't cd to %s", dest);
2441 /* NOTREACHED */
2442 out:
2443 if (flags & CD_PRINT)
2444 out1fmt(snlfmt, curdir);
2445 return 0;
2446}
2447
2448static int
2449pwdcmd(int argc, char **argv)
2450{
2451 int flags;
2452 const char *dir = curdir;
2453
2454 flags = cdopt();
2455 if (flags) {
2456 if (physdir == nullstr)
2457 setpwd(dir, 0);
2458 dir = physdir;
2459 }
2460 out1fmt(snlfmt, dir);
2461 return 0;
2462}
2463
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002464
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002465/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002466
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002467#define IBUFSIZ (BUFSIZ + 1)
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002468#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002469
Eric Andersenc470f442003-07-28 09:56:35 +00002470/* Syntax classes */
2471#define CWORD 0 /* character is nothing special */
2472#define CNL 1 /* newline character */
2473#define CBACK 2 /* a backslash character */
2474#define CSQUOTE 3 /* single quote */
2475#define CDQUOTE 4 /* double quote */
2476#define CENDQUOTE 5 /* a terminating quote */
2477#define CBQUOTE 6 /* backwards single quote */
2478#define CVAR 7 /* a dollar sign */
2479#define CENDVAR 8 /* a '}' character */
2480#define CLP 9 /* a left paren in arithmetic */
2481#define CRP 10 /* a right paren in arithmetic */
2482#define CENDFILE 11 /* end of file */
2483#define CCTL 12 /* like CWORD, except it must be escaped */
2484#define CSPCL 13 /* these terminate a word */
2485#define CIGN 14 /* character should be ignored */
2486
Denis Vlasenko131ae172007-02-18 13:00:19 +00002487#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002488#define SYNBASE 130
2489#define PEOF -130
2490#define PEOA -129
2491#define PEOA_OR_PEOF PEOA
2492#else
2493#define SYNBASE 129
2494#define PEOF -129
2495#define PEOA_OR_PEOF PEOF
2496#endif
2497
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002498/* number syntax index */
2499#define BASESYNTAX 0 /* not in quotes */
2500#define DQSYNTAX 1 /* in double quotes */
2501#define SQSYNTAX 2 /* in single quotes */
2502#define ARISYNTAX 3 /* in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002503
Denis Vlasenko131ae172007-02-18 13:00:19 +00002504#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002505#define USE_SIT_FUNCTION
2506#endif
2507
Denis Vlasenko131ae172007-02-18 13:00:19 +00002508#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002509static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002510#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002511 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002512#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002513 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2514 { CNL, CNL, CNL, CNL }, /* 2, \n */
2515 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2516 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2517 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2518 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2519 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2520 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2521 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2522 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2523 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002524#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002525 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2526 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2527 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002528#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002529};
Eric Andersenc470f442003-07-28 09:56:35 +00002530#else
2531static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002532#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002533 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002534#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002535 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2536 { CNL, CNL, CNL }, /* 2, \n */
2537 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2538 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2539 { CVAR, CVAR, CWORD }, /* 5, $ */
2540 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2541 { CSPCL, CWORD, CWORD }, /* 7, ( */
2542 { CSPCL, CWORD, CWORD }, /* 8, ) */
2543 { CBACK, CBACK, CCTL }, /* 9, \ */
2544 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2545 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002546#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002547 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2548 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2549 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002550#endif
2551};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002552#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002553
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002554#ifdef USE_SIT_FUNCTION
2555
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002556static int
2557SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002558{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002559 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002560#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002561 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002562 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2563 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2564 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2565 11, 3 /* "}~" */
2566 };
2567#else
2568 static const char syntax_index_table[] = {
2569 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2570 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2571 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2572 10, 2 /* "}~" */
2573 };
2574#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002575 const char *s;
2576 int indx;
2577
Eric Andersenc470f442003-07-28 09:56:35 +00002578 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002579 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002580#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002581 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002582 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002583 else
2584#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002585#define U_C(c) ((unsigned char)(c))
2586
2587 if ((unsigned char)c >= (unsigned char)(CTLESC)
2588 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2589 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002590 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002591 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002592 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002593 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002594 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002595 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002596 }
2597 return S_I_T[indx][syntax];
2598}
2599
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002600#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002601
Denis Vlasenko131ae172007-02-18 13:00:19 +00002602#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002603#define CSPCL_CIGN_CIGN_CIGN 0
2604#define CSPCL_CWORD_CWORD_CWORD 1
2605#define CNL_CNL_CNL_CNL 2
2606#define CWORD_CCTL_CCTL_CWORD 3
2607#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2608#define CVAR_CVAR_CWORD_CVAR 5
2609#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2610#define CSPCL_CWORD_CWORD_CLP 7
2611#define CSPCL_CWORD_CWORD_CRP 8
2612#define CBACK_CBACK_CCTL_CBACK 9
2613#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2614#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2615#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2616#define CWORD_CWORD_CWORD_CWORD 13
2617#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002618#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002619#define CSPCL_CWORD_CWORD_CWORD 0
2620#define CNL_CNL_CNL_CNL 1
2621#define CWORD_CCTL_CCTL_CWORD 2
2622#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2623#define CVAR_CVAR_CWORD_CVAR 4
2624#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2625#define CSPCL_CWORD_CWORD_CLP 6
2626#define CSPCL_CWORD_CWORD_CRP 7
2627#define CBACK_CBACK_CCTL_CBACK 8
2628#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2629#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2630#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2631#define CWORD_CWORD_CWORD_CWORD 12
2632#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002633#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002634
2635static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002636 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002637 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002638#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002639 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2640#endif
2641 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2642 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2643 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2644 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2645 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2646 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2647 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2648 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2649 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002650 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2651 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2652 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2653 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2654 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2655 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2656 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2657 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2658 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2659 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2660 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2661 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2662 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2663 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2664 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2665 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2666 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2667 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2668 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2669 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2670 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2671 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2672 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2673 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2674 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2675 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2676 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2677 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2678 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2679 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2680 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2681 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2682 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2683 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2684 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2685 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2686 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2687 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2688 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2689 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2690 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2691 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2692 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2693 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2694 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2695 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2696 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2697 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2698 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2699 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2700 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2701 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2702 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2703 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2704 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2705 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2706 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2707 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2708 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2709 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2710 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2711 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2712 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2713 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2714 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2715 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2717 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2718 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2719 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2720 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2721 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2722 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2724 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2779 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2780 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2802 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002803 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002804 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2805 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2806 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2807 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002808 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002809 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2810 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2811 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2812 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2813 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2814 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2815 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2816 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2817 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2818 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2819 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2820 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2821 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2822 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2823 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2824 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2825 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2826 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2827 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2828 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2829 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2830 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2831 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2832 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2833 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2834 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2835 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2836 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2837 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2838 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2839 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2840 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2841 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2842 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2843 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2844 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2845 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2846 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2847 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2848 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2849 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2850 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2851 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2852 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2853 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2854 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2855 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2856 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2857 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2858 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2859 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2860 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2861 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2862 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2863 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2864 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2865 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2866 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2867 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2868 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2869 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2870 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2871 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2872 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2873 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2875 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2877 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2878 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2880 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2883 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2885 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2894 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2895 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2896 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002897};
2898
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002899#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2900
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002901#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002902
Eric Andersen2870d962001-07-02 17:27:21 +00002903
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002904/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002905
Denis Vlasenko131ae172007-02-18 13:00:19 +00002906#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002907
2908#define ALIASINUSE 1
2909#define ALIASDEAD 2
2910
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002911#define ATABSIZE 39
2912
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002913struct alias {
2914 struct alias *next;
2915 char *name;
2916 char *val;
2917 int flag;
2918};
2919
Eric Andersen2870d962001-07-02 17:27:21 +00002920static struct alias *atab[ATABSIZE];
2921
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002922static struct alias **
2923__lookupalias(const char *name) {
2924 unsigned int hashval;
2925 struct alias **app;
2926 const char *p;
2927 unsigned int ch;
2928
2929 p = name;
2930
2931 ch = (unsigned char)*p;
2932 hashval = ch << 4;
2933 while (ch) {
2934 hashval += ch;
2935 ch = (unsigned char)*++p;
2936 }
2937 app = &atab[hashval % ATABSIZE];
2938
2939 for (; *app; app = &(*app)->next) {
2940 if (strcmp(name, (*app)->name) == 0) {
2941 break;
2942 }
2943 }
2944
2945 return app;
2946}
2947
2948static struct alias *
2949lookupalias(const char *name, int check)
2950{
2951 struct alias *ap = *__lookupalias(name);
2952
2953 if (check && ap && (ap->flag & ALIASINUSE))
2954 return NULL;
2955 return ap;
2956}
2957
2958static struct alias *
2959freealias(struct alias *ap)
2960{
2961 struct alias *next;
2962
2963 if (ap->flag & ALIASINUSE) {
2964 ap->flag |= ALIASDEAD;
2965 return ap;
2966 }
2967
2968 next = ap->next;
2969 free(ap->name);
2970 free(ap->val);
2971 free(ap);
2972 return next;
2973}
Eric Andersencb57d552001-06-28 07:25:16 +00002974
Eric Andersenc470f442003-07-28 09:56:35 +00002975static void
2976setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00002977{
2978 struct alias *ap, **app;
2979
2980 app = __lookupalias(name);
2981 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00002982 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002983 if (ap) {
2984 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00002985 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00002986 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002987 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002988 ap->flag &= ~ALIASDEAD;
2989 } else {
2990 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002991 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002992 ap->name = ckstrdup(name);
2993 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00002994 ap->flag = 0;
2995 ap->next = 0;
2996 *app = ap;
2997 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00002998 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00002999}
3000
Eric Andersenc470f442003-07-28 09:56:35 +00003001static int
3002unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003003{
Eric Andersencb57d552001-06-28 07:25:16 +00003004 struct alias **app;
3005
3006 app = __lookupalias(name);
3007
3008 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003009 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003010 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003011 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003012 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003013 }
3014
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003015 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003016}
3017
Eric Andersenc470f442003-07-28 09:56:35 +00003018static void
3019rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003020{
Eric Andersencb57d552001-06-28 07:25:16 +00003021 struct alias *ap, **app;
3022 int i;
3023
Denis Vlasenkob012b102007-02-19 22:43:01 +00003024 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003025 for (i = 0; i < ATABSIZE; i++) {
3026 app = &atab[i];
3027 for (ap = *app; ap; ap = *app) {
3028 *app = freealias(*app);
3029 if (ap == *app) {
3030 app = &ap->next;
3031 }
3032 }
3033 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003034 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003035}
3036
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003037static void
3038printalias(const struct alias *ap)
3039{
3040 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3041}
3042
Eric Andersencb57d552001-06-28 07:25:16 +00003043/*
3044 * TODO - sort output
3045 */
Eric Andersenc470f442003-07-28 09:56:35 +00003046static int
3047aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003048{
3049 char *n, *v;
3050 int ret = 0;
3051 struct alias *ap;
3052
3053 if (argc == 1) {
3054 int i;
3055
3056 for (i = 0; i < ATABSIZE; i++)
3057 for (ap = atab[i]; ap; ap = ap->next) {
3058 printalias(ap);
3059 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003060 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003061 }
3062 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003063 v = strchr(n+1, '=');
3064 if (v == NULL) { /* n+1: funny ksh stuff */
3065 ap = *__lookupalias(n);
3066 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003067 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003068 ret = 1;
3069 } else
3070 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003071 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003072 *v++ = '\0';
3073 setalias(n, v);
3074 }
3075 }
3076
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003077 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003078}
3079
Eric Andersenc470f442003-07-28 09:56:35 +00003080static int
3081unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003082{
3083 int i;
3084
3085 while ((i = nextopt("a")) != '\0') {
3086 if (i == 'a') {
3087 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003088 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003089 }
3090 }
3091 for (i = 0; *argptr; argptr++) {
3092 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003093 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003094 i = 1;
3095 }
3096 }
3097
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003098 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003099}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003100
Denis Vlasenko131ae172007-02-18 13:00:19 +00003101#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003102
Eric Andersenc470f442003-07-28 09:56:35 +00003103
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003104/* ============ jobs.c */
3105
3106/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3107#define FORK_FG 0
3108#define FORK_BG 1
3109#define FORK_NOJOB 2
3110
3111/* mode flags for showjob(s) */
3112#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3113#define SHOW_PID 0x04 /* include process pid */
3114#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3115
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003116/*
3117 * A job structure contains information about a job. A job is either a
3118 * single process or a set of processes contained in a pipeline. In the
3119 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3120 * array of pids.
3121 */
3122
3123struct procstat {
3124 pid_t pid; /* process id */
3125 int status; /* last process status from wait() */
3126 char *cmd; /* text of command being run */
3127};
3128
3129struct job {
3130 struct procstat ps0; /* status of process */
3131 struct procstat *ps; /* status or processes when more than one */
3132#if JOBS
3133 int stopstatus; /* status of a stopped job */
3134#endif
3135 uint32_t
3136 nprocs: 16, /* number of processes */
3137 state: 8,
3138#define JOBRUNNING 0 /* at least one proc running */
3139#define JOBSTOPPED 1 /* all procs are stopped */
3140#define JOBDONE 2 /* all procs are completed */
3141#if JOBS
3142 sigint: 1, /* job was killed by SIGINT */
3143 jobctl: 1, /* job running under job control */
3144#endif
3145 waited: 1, /* true if this entry has been waited for */
3146 used: 1, /* true if this entry is in used */
3147 changed: 1; /* true if status has changed */
3148 struct job *prev_job; /* previous job */
3149};
3150
3151static pid_t backgndpid; /* pid of last background process */
3152static int job_warning; /* user was warned about stopped jobs */
3153#if JOBS
3154static int jobctl; /* true if doing job control */
3155#endif
3156
3157static struct job *makejob(union node *, int);
3158static int forkshell(struct job *, union node *, int);
3159static int waitforjob(struct job *);
3160
3161#if ! JOBS
3162#define setjobctl(on) /* do nothing */
3163#else
3164static void setjobctl(int);
3165static void showjobs(FILE *, int);
3166#endif
3167
3168/*
3169 * Set the signal handler for the specified signal. The routine figures
3170 * out what it should be set to.
3171 */
3172static void
3173setsignal(int signo)
3174{
3175 int action;
3176 char *t, tsig;
3177 struct sigaction act;
3178
3179 t = trap[signo];
3180 if (t == NULL)
3181 action = S_DFL;
3182 else if (*t != '\0')
3183 action = S_CATCH;
3184 else
3185 action = S_IGN;
3186 if (rootshell && action == S_DFL) {
3187 switch (signo) {
3188 case SIGINT:
3189 if (iflag || minusc || sflag == 0)
3190 action = S_CATCH;
3191 break;
3192 case SIGQUIT:
3193#if DEBUG
3194 if (debug)
3195 break;
3196#endif
3197 /* FALLTHROUGH */
3198 case SIGTERM:
3199 if (iflag)
3200 action = S_IGN;
3201 break;
3202#if JOBS
3203 case SIGTSTP:
3204 case SIGTTOU:
3205 if (mflag)
3206 action = S_IGN;
3207 break;
3208#endif
3209 }
3210 }
3211
3212 t = &sigmode[signo - 1];
3213 tsig = *t;
3214 if (tsig == 0) {
3215 /*
3216 * current setting unknown
3217 */
3218 if (sigaction(signo, 0, &act) == -1) {
3219 /*
3220 * Pretend it worked; maybe we should give a warning
3221 * here, but other shells don't. We don't alter
3222 * sigmode, so that we retry every time.
3223 */
3224 return;
3225 }
3226 if (act.sa_handler == SIG_IGN) {
3227 if (mflag
3228 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3229 ) {
3230 tsig = S_IGN; /* don't hard ignore these */
3231 } else
3232 tsig = S_HARD_IGN;
3233 } else {
3234 tsig = S_RESET; /* force to be set */
3235 }
3236 }
3237 if (tsig == S_HARD_IGN || tsig == action)
3238 return;
3239 switch (action) {
3240 case S_CATCH:
3241 act.sa_handler = onsig;
3242 break;
3243 case S_IGN:
3244 act.sa_handler = SIG_IGN;
3245 break;
3246 default:
3247 act.sa_handler = SIG_DFL;
3248 }
3249 *t = action;
3250 act.sa_flags = 0;
3251 sigfillset(&act.sa_mask);
3252 sigaction(signo, &act, 0);
3253}
3254
3255/* mode flags for set_curjob */
3256#define CUR_DELETE 2
3257#define CUR_RUNNING 1
3258#define CUR_STOPPED 0
3259
3260/* mode flags for dowait */
3261#define DOWAIT_NORMAL 0
3262#define DOWAIT_BLOCK 1
3263
3264#if JOBS
3265/* pgrp of shell on invocation */
3266static int initialpgrp;
3267static int ttyfd = -1;
3268#endif
3269/* array of jobs */
3270static struct job *jobtab;
3271/* size of array */
3272static unsigned njobs;
3273/* current job */
3274static struct job *curjob;
3275/* number of presumed living untracked jobs */
3276static int jobless;
3277
3278static void
3279set_curjob(struct job *jp, unsigned mode)
3280{
3281 struct job *jp1;
3282 struct job **jpp, **curp;
3283
3284 /* first remove from list */
3285 jpp = curp = &curjob;
3286 do {
3287 jp1 = *jpp;
3288 if (jp1 == jp)
3289 break;
3290 jpp = &jp1->prev_job;
3291 } while (1);
3292 *jpp = jp1->prev_job;
3293
3294 /* Then re-insert in correct position */
3295 jpp = curp;
3296 switch (mode) {
3297 default:
3298#if DEBUG
3299 abort();
3300#endif
3301 case CUR_DELETE:
3302 /* job being deleted */
3303 break;
3304 case CUR_RUNNING:
3305 /* newly created job or backgrounded job,
3306 put after all stopped jobs. */
3307 do {
3308 jp1 = *jpp;
3309#if JOBS
3310 if (!jp1 || jp1->state != JOBSTOPPED)
3311#endif
3312 break;
3313 jpp = &jp1->prev_job;
3314 } while (1);
3315 /* FALLTHROUGH */
3316#if JOBS
3317 case CUR_STOPPED:
3318#endif
3319 /* newly stopped job - becomes curjob */
3320 jp->prev_job = *jpp;
3321 *jpp = jp;
3322 break;
3323 }
3324}
3325
3326#if JOBS || DEBUG
3327static int
3328jobno(const struct job *jp)
3329{
3330 return jp - jobtab + 1;
3331}
3332#endif
3333
3334/*
3335 * Convert a job name to a job structure.
3336 */
3337static struct job *
3338getjob(const char *name, int getctl)
3339{
3340 struct job *jp;
3341 struct job *found;
3342 const char *err_msg = "No such job: %s";
3343 unsigned num;
3344 int c;
3345 const char *p;
3346 char *(*match)(const char *, const char *);
3347
3348 jp = curjob;
3349 p = name;
3350 if (!p)
3351 goto currentjob;
3352
3353 if (*p != '%')
3354 goto err;
3355
3356 c = *++p;
3357 if (!c)
3358 goto currentjob;
3359
3360 if (!p[1]) {
3361 if (c == '+' || c == '%') {
3362 currentjob:
3363 err_msg = "No current job";
3364 goto check;
3365 }
3366 if (c == '-') {
3367 if (jp)
3368 jp = jp->prev_job;
3369 err_msg = "No previous job";
3370 check:
3371 if (!jp)
3372 goto err;
3373 goto gotit;
3374 }
3375 }
3376
3377 if (is_number(p)) {
3378 num = atoi(p);
3379 if (num < njobs) {
3380 jp = jobtab + num - 1;
3381 if (jp->used)
3382 goto gotit;
3383 goto err;
3384 }
3385 }
3386
3387 match = prefix;
3388 if (*p == '?') {
3389 match = strstr;
3390 p++;
3391 }
3392
3393 found = 0;
3394 while (1) {
3395 if (!jp)
3396 goto err;
3397 if (match(jp->ps[0].cmd, p)) {
3398 if (found)
3399 goto err;
3400 found = jp;
3401 err_msg = "%s: ambiguous";
3402 }
3403 jp = jp->prev_job;
3404 }
3405
3406 gotit:
3407#if JOBS
3408 err_msg = "job %s not created under job control";
3409 if (getctl && jp->jobctl == 0)
3410 goto err;
3411#endif
3412 return jp;
3413 err:
3414 ash_msg_and_raise_error(err_msg, name);
3415}
3416
3417/*
3418 * Mark a job structure as unused.
3419 */
3420static void
3421freejob(struct job *jp)
3422{
3423 struct procstat *ps;
3424 int i;
3425
3426 INT_OFF;
3427 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3428 if (ps->cmd != nullstr)
3429 free(ps->cmd);
3430 }
3431 if (jp->ps != &jp->ps0)
3432 free(jp->ps);
3433 jp->used = 0;
3434 set_curjob(jp, CUR_DELETE);
3435 INT_ON;
3436}
3437
3438#if JOBS
3439static void
3440xtcsetpgrp(int fd, pid_t pgrp)
3441{
3442 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003443 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003444}
3445
3446/*
3447 * Turn job control on and off.
3448 *
3449 * Note: This code assumes that the third arg to ioctl is a character
3450 * pointer, which is true on Berkeley systems but not System V. Since
3451 * System V doesn't have job control yet, this isn't a problem now.
3452 *
3453 * Called with interrupts off.
3454 */
3455static void
3456setjobctl(int on)
3457{
3458 int fd;
3459 int pgrp;
3460
3461 if (on == jobctl || rootshell == 0)
3462 return;
3463 if (on) {
3464 int ofd;
3465 ofd = fd = open(_PATH_TTY, O_RDWR);
3466 if (fd < 0) {
3467 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3468 * That sometimes helps to acquire controlling tty.
3469 * Obviously, a workaround for bugs when someone
3470 * failed to provide a controlling tty to bash! :) */
3471 fd += 3;
3472 while (!isatty(fd) && --fd >= 0)
3473 ;
3474 }
3475 fd = fcntl(fd, F_DUPFD, 10);
3476 close(ofd);
3477 if (fd < 0)
3478 goto out;
3479 fcntl(fd, F_SETFD, FD_CLOEXEC);
3480 do { /* while we are in the background */
3481 pgrp = tcgetpgrp(fd);
3482 if (pgrp < 0) {
3483 out:
3484 ash_msg("can't access tty; job control turned off");
3485 mflag = on = 0;
3486 goto close;
3487 }
3488 if (pgrp == getpgrp())
3489 break;
3490 killpg(0, SIGTTIN);
3491 } while (1);
3492 initialpgrp = pgrp;
3493
3494 setsignal(SIGTSTP);
3495 setsignal(SIGTTOU);
3496 setsignal(SIGTTIN);
3497 pgrp = rootpid;
3498 setpgid(0, pgrp);
3499 xtcsetpgrp(fd, pgrp);
3500 } else {
3501 /* turning job control off */
3502 fd = ttyfd;
3503 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003504 /* was xtcsetpgrp, but this can make exiting ash
3505 * with pty already deleted loop forever */
3506 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003507 setpgid(0, pgrp);
3508 setsignal(SIGTSTP);
3509 setsignal(SIGTTOU);
3510 setsignal(SIGTTIN);
3511 close:
3512 close(fd);
3513 fd = -1;
3514 }
3515 ttyfd = fd;
3516 jobctl = on;
3517}
3518
3519static int
3520killcmd(int argc, char **argv)
3521{
3522 int signo = -1;
3523 int list = 0;
3524 int i;
3525 pid_t pid;
3526 struct job *jp;
3527
3528 if (argc <= 1) {
3529 usage:
3530 ash_msg_and_raise_error(
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003531"usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003532"kill -l [exitstatus]"
3533 );
3534 }
3535
3536 if (**++argv == '-') {
3537 signo = get_signum(*argv + 1);
3538 if (signo < 0) {
3539 int c;
3540
3541 while ((c = nextopt("ls:")) != '\0') {
3542 switch (c) {
3543 default:
3544#if DEBUG
3545 abort();
3546#endif
3547 case 'l':
3548 list = 1;
3549 break;
3550 case 's':
3551 signo = get_signum(optionarg);
3552 if (signo < 0) {
3553 ash_msg_and_raise_error(
3554 "invalid signal number or name: %s",
3555 optionarg
3556 );
3557 }
3558 break;
3559 }
3560 }
3561 argv = argptr;
3562 } else
3563 argv++;
3564 }
3565
3566 if (!list && signo < 0)
3567 signo = SIGTERM;
3568
3569 if ((signo < 0 || !*argv) ^ list) {
3570 goto usage;
3571 }
3572
3573 if (list) {
3574 const char *name;
3575
3576 if (!*argv) {
3577 for (i = 1; i < NSIG; i++) {
3578 name = get_signame(i);
Denis Vlasenkod7c81962007-04-11 20:43:31 +00003579 if (!isdigit(*name))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003580 out1fmt(snlfmt, name);
3581 }
3582 return 0;
3583 }
3584 name = get_signame(signo);
3585 if (!isdigit(*name))
3586 ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
3587 out1fmt(snlfmt, name);
3588 return 0;
3589 }
3590
3591 i = 0;
3592 do {
3593 if (**argv == '%') {
3594 jp = getjob(*argv, 0);
3595 pid = -jp->ps[0].pid;
3596 } else {
3597 pid = **argv == '-' ?
3598 -number(*argv + 1) : number(*argv);
3599 }
3600 if (kill(pid, signo) != 0) {
3601 ash_msg("(%d) - %m", pid);
3602 i = 1;
3603 }
3604 } while (*++argv);
3605
3606 return i;
3607}
3608
3609static void
3610showpipe(struct job *jp, FILE *out)
3611{
3612 struct procstat *sp;
3613 struct procstat *spend;
3614
3615 spend = jp->ps + jp->nprocs;
3616 for (sp = jp->ps + 1; sp < spend; sp++)
3617 fprintf(out, " | %s", sp->cmd);
3618 outcslow('\n', out);
3619 flush_stdout_stderr();
3620}
3621
3622
3623static int
3624restartjob(struct job *jp, int mode)
3625{
3626 struct procstat *ps;
3627 int i;
3628 int status;
3629 pid_t pgid;
3630
3631 INT_OFF;
3632 if (jp->state == JOBDONE)
3633 goto out;
3634 jp->state = JOBRUNNING;
3635 pgid = jp->ps->pid;
3636 if (mode == FORK_FG)
3637 xtcsetpgrp(ttyfd, pgid);
3638 killpg(pgid, SIGCONT);
3639 ps = jp->ps;
3640 i = jp->nprocs;
3641 do {
3642 if (WIFSTOPPED(ps->status)) {
3643 ps->status = -1;
3644 }
3645 } while (ps++, --i);
3646 out:
3647 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3648 INT_ON;
3649 return status;
3650}
3651
3652static int
3653fg_bgcmd(int argc, char **argv)
3654{
3655 struct job *jp;
3656 FILE *out;
3657 int mode;
3658 int retval;
3659
3660 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3661 nextopt(nullstr);
3662 argv = argptr;
3663 out = stdout;
3664 do {
3665 jp = getjob(*argv, 1);
3666 if (mode == FORK_BG) {
3667 set_curjob(jp, CUR_RUNNING);
3668 fprintf(out, "[%d] ", jobno(jp));
3669 }
3670 outstr(jp->ps->cmd, out);
3671 showpipe(jp, out);
3672 retval = restartjob(jp, mode);
3673 } while (*argv && *++argv);
3674 return retval;
3675}
3676#endif
3677
3678static int
3679sprint_status(char *s, int status, int sigonly)
3680{
3681 int col;
3682 int st;
3683
3684 col = 0;
3685 if (!WIFEXITED(status)) {
3686#if JOBS
3687 if (WIFSTOPPED(status))
3688 st = WSTOPSIG(status);
3689 else
3690#endif
3691 st = WTERMSIG(status);
3692 if (sigonly) {
3693 if (st == SIGINT || st == SIGPIPE)
3694 goto out;
3695#if JOBS
3696 if (WIFSTOPPED(status))
3697 goto out;
3698#endif
3699 }
3700 st &= 0x7f;
3701 col = fmtstr(s, 32, strsignal(st));
3702 if (WCOREDUMP(status)) {
3703 col += fmtstr(s + col, 16, " (core dumped)");
3704 }
3705 } else if (!sigonly) {
3706 st = WEXITSTATUS(status);
3707 if (st)
3708 col = fmtstr(s, 16, "Done(%d)", st);
3709 else
3710 col = fmtstr(s, 16, "Done");
3711 }
3712 out:
3713 return col;
3714}
3715
3716/*
3717 * Do a wait system call. If job control is compiled in, we accept
3718 * stopped processes. If block is zero, we return a value of zero
3719 * rather than blocking.
3720 *
3721 * System V doesn't have a non-blocking wait system call. It does
3722 * have a SIGCLD signal that is sent to a process when one of it's
3723 * children dies. The obvious way to use SIGCLD would be to install
3724 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3725 * was received, and have waitproc bump another counter when it got
3726 * the status of a process. Waitproc would then know that a wait
3727 * system call would not block if the two counters were different.
3728 * This approach doesn't work because if a process has children that
3729 * have not been waited for, System V will send it a SIGCLD when it
3730 * installs a signal handler for SIGCLD. What this means is that when
3731 * a child exits, the shell will be sent SIGCLD signals continuously
3732 * until is runs out of stack space, unless it does a wait call before
3733 * restoring the signal handler. The code below takes advantage of
3734 * this (mis)feature by installing a signal handler for SIGCLD and
3735 * then checking to see whether it was called. If there are any
3736 * children to be waited for, it will be.
3737 *
3738 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3739 * waits at all. In this case, the user will not be informed when
3740 * a background process until the next time she runs a real program
3741 * (as opposed to running a builtin command or just typing return),
3742 * and the jobs command may give out of date information.
3743 */
3744static int
3745waitproc(int block, int *status)
3746{
3747 int flags = 0;
3748
3749#if JOBS
3750 if (jobctl)
3751 flags |= WUNTRACED;
3752#endif
3753 if (block == 0)
3754 flags |= WNOHANG;
3755 return wait3(status, flags, (struct rusage *)NULL);
3756}
3757
3758/*
3759 * Wait for a process to terminate.
3760 */
3761static int
3762dowait(int block, struct job *job)
3763{
3764 int pid;
3765 int status;
3766 struct job *jp;
3767 struct job *thisjob;
3768 int state;
3769
3770 TRACE(("dowait(%d) called\n", block));
3771 pid = waitproc(block, &status);
3772 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3773 if (pid <= 0)
3774 return pid;
3775 INT_OFF;
3776 thisjob = NULL;
3777 for (jp = curjob; jp; jp = jp->prev_job) {
3778 struct procstat *sp;
3779 struct procstat *spend;
3780 if (jp->state == JOBDONE)
3781 continue;
3782 state = JOBDONE;
3783 spend = jp->ps + jp->nprocs;
3784 sp = jp->ps;
3785 do {
3786 if (sp->pid == pid) {
3787 TRACE(("Job %d: changing status of proc %d "
3788 "from 0x%x to 0x%x\n",
3789 jobno(jp), pid, sp->status, status));
3790 sp->status = status;
3791 thisjob = jp;
3792 }
3793 if (sp->status == -1)
3794 state = JOBRUNNING;
3795#if JOBS
3796 if (state == JOBRUNNING)
3797 continue;
3798 if (WIFSTOPPED(sp->status)) {
3799 jp->stopstatus = sp->status;
3800 state = JOBSTOPPED;
3801 }
3802#endif
3803 } while (++sp < spend);
3804 if (thisjob)
3805 goto gotjob;
3806 }
3807#if JOBS
3808 if (!WIFSTOPPED(status))
3809#endif
3810
3811 jobless--;
3812 goto out;
3813
3814 gotjob:
3815 if (state != JOBRUNNING) {
3816 thisjob->changed = 1;
3817
3818 if (thisjob->state != state) {
3819 TRACE(("Job %d: changing state from %d to %d\n",
3820 jobno(thisjob), thisjob->state, state));
3821 thisjob->state = state;
3822#if JOBS
3823 if (state == JOBSTOPPED) {
3824 set_curjob(thisjob, CUR_STOPPED);
3825 }
3826#endif
3827 }
3828 }
3829
3830 out:
3831 INT_ON;
3832
3833 if (thisjob && thisjob == job) {
3834 char s[48 + 1];
3835 int len;
3836
3837 len = sprint_status(s, status, 1);
3838 if (len) {
3839 s[len] = '\n';
3840 s[len + 1] = 0;
3841 out2str(s);
3842 }
3843 }
3844 return pid;
3845}
3846
3847#if JOBS
3848static void
3849showjob(FILE *out, struct job *jp, int mode)
3850{
3851 struct procstat *ps;
3852 struct procstat *psend;
3853 int col;
3854 int indent;
3855 char s[80];
3856
3857 ps = jp->ps;
3858
3859 if (mode & SHOW_PGID) {
3860 /* just output process (group) id of pipeline */
3861 fprintf(out, "%d\n", ps->pid);
3862 return;
3863 }
3864
3865 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3866 indent = col;
3867
3868 if (jp == curjob)
3869 s[col - 2] = '+';
3870 else if (curjob && jp == curjob->prev_job)
3871 s[col - 2] = '-';
3872
3873 if (mode & SHOW_PID)
3874 col += fmtstr(s + col, 16, "%d ", ps->pid);
3875
3876 psend = ps + jp->nprocs;
3877
3878 if (jp->state == JOBRUNNING) {
3879 strcpy(s + col, "Running");
3880 col += sizeof("Running") - 1;
3881 } else {
3882 int status = psend[-1].status;
3883 if (jp->state == JOBSTOPPED)
3884 status = jp->stopstatus;
3885 col += sprint_status(s + col, status, 0);
3886 }
3887
3888 goto start;
3889
3890 do {
3891 /* for each process */
3892 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
3893 start:
3894 fprintf(out, "%s%*c%s",
3895 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3896 );
3897 if (!(mode & SHOW_PID)) {
3898 showpipe(jp, out);
3899 break;
3900 }
3901 if (++ps == psend) {
3902 outcslow('\n', out);
3903 break;
3904 }
3905 } while (1);
3906
3907 jp->changed = 0;
3908
3909 if (jp->state == JOBDONE) {
3910 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3911 freejob(jp);
3912 }
3913}
3914
3915static int
3916jobscmd(int argc, char **argv)
3917{
3918 int mode, m;
3919 FILE *out;
3920
3921 mode = 0;
3922 while ((m = nextopt("lp"))) {
3923 if (m == 'l')
3924 mode = SHOW_PID;
3925 else
3926 mode = SHOW_PGID;
3927 }
3928
3929 out = stdout;
3930 argv = argptr;
3931 if (*argv) {
3932 do
3933 showjob(out, getjob(*argv,0), mode);
3934 while (*++argv);
3935 } else
3936 showjobs(out, mode);
3937
3938 return 0;
3939}
3940
3941/*
3942 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3943 * statuses have changed since the last call to showjobs.
3944 */
3945static void
3946showjobs(FILE *out, int mode)
3947{
3948 struct job *jp;
3949
3950 TRACE(("showjobs(%x) called\n", mode));
3951
3952 /* If not even one one job changed, there is nothing to do */
3953 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3954 continue;
3955
3956 for (jp = curjob; jp; jp = jp->prev_job) {
3957 if (!(mode & SHOW_CHANGED) || jp->changed)
3958 showjob(out, jp, mode);
3959 }
3960}
3961#endif /* JOBS */
3962
3963static int
3964getstatus(struct job *job)
3965{
3966 int status;
3967 int retval;
3968
3969 status = job->ps[job->nprocs - 1].status;
3970 retval = WEXITSTATUS(status);
3971 if (!WIFEXITED(status)) {
3972#if JOBS
3973 retval = WSTOPSIG(status);
3974 if (!WIFSTOPPED(status))
3975#endif
3976 {
3977 /* XXX: limits number of signals */
3978 retval = WTERMSIG(status);
3979#if JOBS
3980 if (retval == SIGINT)
3981 job->sigint = 1;
3982#endif
3983 }
3984 retval += 128;
3985 }
3986 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3987 jobno(job), job->nprocs, status, retval));
3988 return retval;
3989}
3990
3991static int
3992waitcmd(int argc, char **argv)
3993{
3994 struct job *job;
3995 int retval;
3996 struct job *jp;
3997
3998 EXSIGON;
3999
4000 nextopt(nullstr);
4001 retval = 0;
4002
4003 argv = argptr;
4004 if (!*argv) {
4005 /* wait for all jobs */
4006 for (;;) {
4007 jp = curjob;
4008 while (1) {
4009 if (!jp) {
4010 /* no running procs */
4011 goto out;
4012 }
4013 if (jp->state == JOBRUNNING)
4014 break;
4015 jp->waited = 1;
4016 jp = jp->prev_job;
4017 }
4018 dowait(DOWAIT_BLOCK, 0);
4019 }
4020 }
4021
4022 retval = 127;
4023 do {
4024 if (**argv != '%') {
4025 pid_t pid = number(*argv);
4026 job = curjob;
4027 goto start;
4028 do {
4029 if (job->ps[job->nprocs - 1].pid == pid)
4030 break;
4031 job = job->prev_job;
4032 start:
4033 if (!job)
4034 goto repeat;
4035 } while (1);
4036 } else
4037 job = getjob(*argv, 0);
4038 /* loop until process terminated or stopped */
4039 while (job->state == JOBRUNNING)
4040 dowait(DOWAIT_BLOCK, 0);
4041 job->waited = 1;
4042 retval = getstatus(job);
4043 repeat:
4044 ;
4045 } while (*++argv);
4046
4047 out:
4048 return retval;
4049}
4050
4051static struct job *
4052growjobtab(void)
4053{
4054 size_t len;
4055 ptrdiff_t offset;
4056 struct job *jp, *jq;
4057
4058 len = njobs * sizeof(*jp);
4059 jq = jobtab;
4060 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4061
4062 offset = (char *)jp - (char *)jq;
4063 if (offset) {
4064 /* Relocate pointers */
4065 size_t l = len;
4066
4067 jq = (struct job *)((char *)jq + l);
4068 while (l) {
4069 l -= sizeof(*jp);
4070 jq--;
4071#define joff(p) ((struct job *)((char *)(p) + l))
4072#define jmove(p) (p) = (void *)((char *)(p) + offset)
4073 if (joff(jp)->ps == &jq->ps0)
4074 jmove(joff(jp)->ps);
4075 if (joff(jp)->prev_job)
4076 jmove(joff(jp)->prev_job);
4077 }
4078 if (curjob)
4079 jmove(curjob);
4080#undef joff
4081#undef jmove
4082 }
4083
4084 njobs += 4;
4085 jobtab = jp;
4086 jp = (struct job *)((char *)jp + len);
4087 jq = jp + 3;
4088 do {
4089 jq->used = 0;
4090 } while (--jq >= jp);
4091 return jp;
4092}
4093
4094/*
4095 * Return a new job structure.
4096 * Called with interrupts off.
4097 */
4098static struct job *
4099makejob(union node *node, int nprocs)
4100{
4101 int i;
4102 struct job *jp;
4103
4104 for (i = njobs, jp = jobtab; ; jp++) {
4105 if (--i < 0) {
4106 jp = growjobtab();
4107 break;
4108 }
4109 if (jp->used == 0)
4110 break;
4111 if (jp->state != JOBDONE || !jp->waited)
4112 continue;
4113#if JOBS
4114 if (jobctl)
4115 continue;
4116#endif
4117 freejob(jp);
4118 break;
4119 }
4120 memset(jp, 0, sizeof(*jp));
4121#if JOBS
4122 if (jobctl)
4123 jp->jobctl = 1;
4124#endif
4125 jp->prev_job = curjob;
4126 curjob = jp;
4127 jp->used = 1;
4128 jp->ps = &jp->ps0;
4129 if (nprocs > 1) {
4130 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4131 }
4132 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4133 jobno(jp)));
4134 return jp;
4135}
4136
4137#if JOBS
4138/*
4139 * Return a string identifying a command (to be printed by the
4140 * jobs command).
4141 */
4142static char *cmdnextc;
4143
4144static void
4145cmdputs(const char *s)
4146{
4147 const char *p, *str;
4148 char c, cc[2] = " ";
4149 char *nextc;
4150 int subtype = 0;
4151 int quoted = 0;
4152 static const char vstype[VSTYPE + 1][4] = {
4153 "", "}", "-", "+", "?", "=",
4154 "%", "%%", "#", "##"
4155 };
4156
4157 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4158 p = s;
4159 while ((c = *p++) != 0) {
4160 str = 0;
4161 switch (c) {
4162 case CTLESC:
4163 c = *p++;
4164 break;
4165 case CTLVAR:
4166 subtype = *p++;
4167 if ((subtype & VSTYPE) == VSLENGTH)
4168 str = "${#";
4169 else
4170 str = "${";
4171 if (!(subtype & VSQUOTE) == !(quoted & 1))
4172 goto dostr;
4173 quoted ^= 1;
4174 c = '"';
4175 break;
4176 case CTLENDVAR:
4177 str = "\"}" + !(quoted & 1);
4178 quoted >>= 1;
4179 subtype = 0;
4180 goto dostr;
4181 case CTLBACKQ:
4182 str = "$(...)";
4183 goto dostr;
4184 case CTLBACKQ+CTLQUOTE:
4185 str = "\"$(...)\"";
4186 goto dostr;
4187#if ENABLE_ASH_MATH_SUPPORT
4188 case CTLARI:
4189 str = "$((";
4190 goto dostr;
4191 case CTLENDARI:
4192 str = "))";
4193 goto dostr;
4194#endif
4195 case CTLQUOTEMARK:
4196 quoted ^= 1;
4197 c = '"';
4198 break;
4199 case '=':
4200 if (subtype == 0)
4201 break;
4202 if ((subtype & VSTYPE) != VSNORMAL)
4203 quoted <<= 1;
4204 str = vstype[subtype & VSTYPE];
4205 if (subtype & VSNUL)
4206 c = ':';
4207 else
4208 goto checkstr;
4209 break;
4210 case '\'':
4211 case '\\':
4212 case '"':
4213 case '$':
4214 /* These can only happen inside quotes */
4215 cc[0] = c;
4216 str = cc;
4217 c = '\\';
4218 break;
4219 default:
4220 break;
4221 }
4222 USTPUTC(c, nextc);
4223 checkstr:
4224 if (!str)
4225 continue;
4226 dostr:
4227 while ((c = *str++)) {
4228 USTPUTC(c, nextc);
4229 }
4230 }
4231 if (quoted & 1) {
4232 USTPUTC('"', nextc);
4233 }
4234 *nextc = 0;
4235 cmdnextc = nextc;
4236}
4237
4238/* cmdtxt() and cmdlist() call each other */
4239static void cmdtxt(union node *n);
4240
4241static void
4242cmdlist(union node *np, int sep)
4243{
4244 for (; np; np = np->narg.next) {
4245 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004246 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004247 cmdtxt(np);
4248 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004249 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004250 }
4251}
4252
4253static void
4254cmdtxt(union node *n)
4255{
4256 union node *np;
4257 struct nodelist *lp;
4258 const char *p;
4259 char s[2];
4260
4261 if (!n)
4262 return;
4263 switch (n->type) {
4264 default:
4265#if DEBUG
4266 abort();
4267#endif
4268 case NPIPE:
4269 lp = n->npipe.cmdlist;
4270 for (;;) {
4271 cmdtxt(lp->n);
4272 lp = lp->next;
4273 if (!lp)
4274 break;
4275 cmdputs(" | ");
4276 }
4277 break;
4278 case NSEMI:
4279 p = "; ";
4280 goto binop;
4281 case NAND:
4282 p = " && ";
4283 goto binop;
4284 case NOR:
4285 p = " || ";
4286 binop:
4287 cmdtxt(n->nbinary.ch1);
4288 cmdputs(p);
4289 n = n->nbinary.ch2;
4290 goto donode;
4291 case NREDIR:
4292 case NBACKGND:
4293 n = n->nredir.n;
4294 goto donode;
4295 case NNOT:
4296 cmdputs("!");
4297 n = n->nnot.com;
4298 donode:
4299 cmdtxt(n);
4300 break;
4301 case NIF:
4302 cmdputs("if ");
4303 cmdtxt(n->nif.test);
4304 cmdputs("; then ");
4305 n = n->nif.ifpart;
4306 if (n->nif.elsepart) {
4307 cmdtxt(n);
4308 cmdputs("; else ");
4309 n = n->nif.elsepart;
4310 }
4311 p = "; fi";
4312 goto dotail;
4313 case NSUBSHELL:
4314 cmdputs("(");
4315 n = n->nredir.n;
4316 p = ")";
4317 goto dotail;
4318 case NWHILE:
4319 p = "while ";
4320 goto until;
4321 case NUNTIL:
4322 p = "until ";
4323 until:
4324 cmdputs(p);
4325 cmdtxt(n->nbinary.ch1);
4326 n = n->nbinary.ch2;
4327 p = "; done";
4328 dodo:
4329 cmdputs("; do ");
4330 dotail:
4331 cmdtxt(n);
4332 goto dotail2;
4333 case NFOR:
4334 cmdputs("for ");
4335 cmdputs(n->nfor.var);
4336 cmdputs(" in ");
4337 cmdlist(n->nfor.args, 1);
4338 n = n->nfor.body;
4339 p = "; done";
4340 goto dodo;
4341 case NDEFUN:
4342 cmdputs(n->narg.text);
4343 p = "() { ... }";
4344 goto dotail2;
4345 case NCMD:
4346 cmdlist(n->ncmd.args, 1);
4347 cmdlist(n->ncmd.redirect, 0);
4348 break;
4349 case NARG:
4350 p = n->narg.text;
4351 dotail2:
4352 cmdputs(p);
4353 break;
4354 case NHERE:
4355 case NXHERE:
4356 p = "<<...";
4357 goto dotail2;
4358 case NCASE:
4359 cmdputs("case ");
4360 cmdputs(n->ncase.expr->narg.text);
4361 cmdputs(" in ");
4362 for (np = n->ncase.cases; np; np = np->nclist.next) {
4363 cmdtxt(np->nclist.pattern);
4364 cmdputs(") ");
4365 cmdtxt(np->nclist.body);
4366 cmdputs(";; ");
4367 }
4368 p = "esac";
4369 goto dotail2;
4370 case NTO:
4371 p = ">";
4372 goto redir;
4373 case NCLOBBER:
4374 p = ">|";
4375 goto redir;
4376 case NAPPEND:
4377 p = ">>";
4378 goto redir;
4379 case NTOFD:
4380 p = ">&";
4381 goto redir;
4382 case NFROM:
4383 p = "<";
4384 goto redir;
4385 case NFROMFD:
4386 p = "<&";
4387 goto redir;
4388 case NFROMTO:
4389 p = "<>";
4390 redir:
4391 s[0] = n->nfile.fd + '0';
4392 s[1] = '\0';
4393 cmdputs(s);
4394 cmdputs(p);
4395 if (n->type == NTOFD || n->type == NFROMFD) {
4396 s[0] = n->ndup.dupfd + '0';
4397 p = s;
4398 goto dotail2;
4399 }
4400 n = n->nfile.fname;
4401 goto donode;
4402 }
4403}
4404
4405static char *
4406commandtext(union node *n)
4407{
4408 char *name;
4409
4410 STARTSTACKSTR(cmdnextc);
4411 cmdtxt(n);
4412 name = stackblock();
4413 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4414 name, cmdnextc, cmdnextc));
4415 return ckstrdup(name);
4416}
4417#endif /* JOBS */
4418
4419/*
4420 * Fork off a subshell. If we are doing job control, give the subshell its
4421 * own process group. Jp is a job structure that the job is to be added to.
4422 * N is the command that will be evaluated by the child. Both jp and n may
4423 * be NULL. The mode parameter can be one of the following:
4424 * FORK_FG - Fork off a foreground process.
4425 * FORK_BG - Fork off a background process.
4426 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4427 * process group even if job control is on.
4428 *
4429 * When job control is turned off, background processes have their standard
4430 * input redirected to /dev/null (except for the second and later processes
4431 * in a pipeline).
4432 *
4433 * Called with interrupts off.
4434 */
4435/*
4436 * Clear traps on a fork.
4437 */
4438static void
4439clear_traps(void)
4440{
4441 char **tp;
4442
4443 for (tp = trap; tp < &trap[NSIG]; tp++) {
4444 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4445 INT_OFF;
4446 free(*tp);
4447 *tp = NULL;
4448 if (tp != &trap[0])
4449 setsignal(tp - trap);
4450 INT_ON;
4451 }
4452 }
4453}
4454/* lives far away from here, needed for forkchild */
4455static void closescript(void);
4456static void
4457forkchild(struct job *jp, union node *n, int mode)
4458{
4459 int oldlvl;
4460
4461 TRACE(("Child shell %d\n", getpid()));
4462 oldlvl = shlvl;
4463 shlvl++;
4464
4465 closescript();
4466 clear_traps();
4467#if JOBS
4468 /* do job control only in root shell */
4469 jobctl = 0;
4470 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4471 pid_t pgrp;
4472
4473 if (jp->nprocs == 0)
4474 pgrp = getpid();
4475 else
4476 pgrp = jp->ps[0].pid;
4477 /* This can fail because we are doing it in the parent also */
4478 (void)setpgid(0, pgrp);
4479 if (mode == FORK_FG)
4480 xtcsetpgrp(ttyfd, pgrp);
4481 setsignal(SIGTSTP);
4482 setsignal(SIGTTOU);
4483 } else
4484#endif
4485 if (mode == FORK_BG) {
4486 ignoresig(SIGINT);
4487 ignoresig(SIGQUIT);
4488 if (jp->nprocs == 0) {
4489 close(0);
4490 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004491 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004492 }
4493 }
4494 if (!oldlvl && iflag) {
4495 setsignal(SIGINT);
4496 setsignal(SIGQUIT);
4497 setsignal(SIGTERM);
4498 }
4499 for (jp = curjob; jp; jp = jp->prev_job)
4500 freejob(jp);
4501 jobless = 0;
4502}
4503
4504static void
4505forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4506{
4507 TRACE(("In parent shell: child = %d\n", pid));
4508 if (!jp) {
4509 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4510 jobless++;
4511 return;
4512 }
4513#if JOBS
4514 if (mode != FORK_NOJOB && jp->jobctl) {
4515 int pgrp;
4516
4517 if (jp->nprocs == 0)
4518 pgrp = pid;
4519 else
4520 pgrp = jp->ps[0].pid;
4521 /* This can fail because we are doing it in the child also */
4522 setpgid(pid, pgrp);
4523 }
4524#endif
4525 if (mode == FORK_BG) {
4526 backgndpid = pid; /* set $! */
4527 set_curjob(jp, CUR_RUNNING);
4528 }
4529 if (jp) {
4530 struct procstat *ps = &jp->ps[jp->nprocs++];
4531 ps->pid = pid;
4532 ps->status = -1;
4533 ps->cmd = nullstr;
4534#if JOBS
4535 if (jobctl && n)
4536 ps->cmd = commandtext(n);
4537#endif
4538 }
4539}
4540
4541static int
4542forkshell(struct job *jp, union node *n, int mode)
4543{
4544 int pid;
4545
4546 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4547 pid = fork();
4548 if (pid < 0) {
4549 TRACE(("Fork failed, errno=%d", errno));
4550 if (jp)
4551 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004552 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004553 }
4554 if (pid == 0)
4555 forkchild(jp, n, mode);
4556 else
4557 forkparent(jp, n, mode, pid);
4558 return pid;
4559}
4560
4561/*
4562 * Wait for job to finish.
4563 *
4564 * Under job control we have the problem that while a child process is
4565 * running interrupts generated by the user are sent to the child but not
4566 * to the shell. This means that an infinite loop started by an inter-
4567 * active user may be hard to kill. With job control turned off, an
4568 * interactive user may place an interactive program inside a loop. If
4569 * the interactive program catches interrupts, the user doesn't want
4570 * these interrupts to also abort the loop. The approach we take here
4571 * is to have the shell ignore interrupt signals while waiting for a
4572 * foreground process to terminate, and then send itself an interrupt
4573 * signal if the child process was terminated by an interrupt signal.
4574 * Unfortunately, some programs want to do a bit of cleanup and then
4575 * exit on interrupt; unless these processes terminate themselves by
4576 * sending a signal to themselves (instead of calling exit) they will
4577 * confuse this approach.
4578 *
4579 * Called with interrupts off.
4580 */
4581static int
4582waitforjob(struct job *jp)
4583{
4584 int st;
4585
4586 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4587 while (jp->state == JOBRUNNING) {
4588 dowait(DOWAIT_BLOCK, jp);
4589 }
4590 st = getstatus(jp);
4591#if JOBS
4592 if (jp->jobctl) {
4593 xtcsetpgrp(ttyfd, rootpid);
4594 /*
4595 * This is truly gross.
4596 * If we're doing job control, then we did a TIOCSPGRP which
4597 * caused us (the shell) to no longer be in the controlling
4598 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4599 * intuit from the subprocess exit status whether a SIGINT
4600 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4601 */
4602 if (jp->sigint)
4603 raise(SIGINT);
4604 }
4605 if (jp->state == JOBDONE)
4606#endif
4607 freejob(jp);
4608 return st;
4609}
4610
4611/*
4612 * return 1 if there are stopped jobs, otherwise 0
4613 */
4614static int
4615stoppedjobs(void)
4616{
4617 struct job *jp;
4618 int retval;
4619
4620 retval = 0;
4621 if (job_warning)
4622 goto out;
4623 jp = curjob;
4624 if (jp && jp->state == JOBSTOPPED) {
4625 out2str("You have stopped jobs.\n");
4626 job_warning = 2;
4627 retval++;
4628 }
4629 out:
4630 return retval;
4631}
4632
4633
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004634/* ============ redir.c
4635 *
4636 * Code for dealing with input/output redirection.
4637 */
4638
4639#define EMPTY -2 /* marks an unused slot in redirtab */
4640#ifndef PIPE_BUF
4641# define PIPESIZE 4096 /* amount of buffering in a pipe */
4642#else
4643# define PIPESIZE PIPE_BUF
4644#endif
4645
4646/*
4647 * Open a file in noclobber mode.
4648 * The code was copied from bash.
4649 */
4650static int
4651noclobberopen(const char *fname)
4652{
4653 int r, fd;
4654 struct stat finfo, finfo2;
4655
4656 /*
4657 * If the file exists and is a regular file, return an error
4658 * immediately.
4659 */
4660 r = stat(fname, &finfo);
4661 if (r == 0 && S_ISREG(finfo.st_mode)) {
4662 errno = EEXIST;
4663 return -1;
4664 }
4665
4666 /*
4667 * If the file was not present (r != 0), make sure we open it
4668 * exclusively so that if it is created before we open it, our open
4669 * will fail. Make sure that we do not truncate an existing file.
4670 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4671 * file was not a regular file, we leave O_EXCL off.
4672 */
4673 if (r != 0)
4674 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4675 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4676
4677 /* If the open failed, return the file descriptor right away. */
4678 if (fd < 0)
4679 return fd;
4680
4681 /*
4682 * OK, the open succeeded, but the file may have been changed from a
4683 * non-regular file to a regular file between the stat and the open.
4684 * We are assuming that the O_EXCL open handles the case where FILENAME
4685 * did not exist and is symlinked to an existing file between the stat
4686 * and open.
4687 */
4688
4689 /*
4690 * If we can open it and fstat the file descriptor, and neither check
4691 * revealed that it was a regular file, and the file has not been
4692 * replaced, return the file descriptor.
4693 */
4694 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4695 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4696 return fd;
4697
4698 /* The file has been replaced. badness. */
4699 close(fd);
4700 errno = EEXIST;
4701 return -1;
4702}
4703
4704/*
4705 * Handle here documents. Normally we fork off a process to write the
4706 * data to a pipe. If the document is short, we can stuff the data in
4707 * the pipe without forking.
4708 */
4709/* openhere needs this forward reference */
4710static void expandhere(union node *arg, int fd);
4711static int
4712openhere(union node *redir)
4713{
4714 int pip[2];
4715 size_t len = 0;
4716
4717 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004718 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004719 if (redir->type == NHERE) {
4720 len = strlen(redir->nhere.doc->narg.text);
4721 if (len <= PIPESIZE) {
4722 full_write(pip[1], redir->nhere.doc->narg.text, len);
4723 goto out;
4724 }
4725 }
4726 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4727 close(pip[0]);
4728 signal(SIGINT, SIG_IGN);
4729 signal(SIGQUIT, SIG_IGN);
4730 signal(SIGHUP, SIG_IGN);
4731#ifdef SIGTSTP
4732 signal(SIGTSTP, SIG_IGN);
4733#endif
4734 signal(SIGPIPE, SIG_DFL);
4735 if (redir->type == NHERE)
4736 full_write(pip[1], redir->nhere.doc->narg.text, len);
4737 else
4738 expandhere(redir->nhere.doc, pip[1]);
4739 _exit(0);
4740 }
4741 out:
4742 close(pip[1]);
4743 return pip[0];
4744}
4745
4746static int
4747openredirect(union node *redir)
4748{
4749 char *fname;
4750 int f;
4751
4752 switch (redir->nfile.type) {
4753 case NFROM:
4754 fname = redir->nfile.expfname;
4755 f = open(fname, O_RDONLY);
4756 if (f < 0)
4757 goto eopen;
4758 break;
4759 case NFROMTO:
4760 fname = redir->nfile.expfname;
4761 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4762 if (f < 0)
4763 goto ecreate;
4764 break;
4765 case NTO:
4766 /* Take care of noclobber mode. */
4767 if (Cflag) {
4768 fname = redir->nfile.expfname;
4769 f = noclobberopen(fname);
4770 if (f < 0)
4771 goto ecreate;
4772 break;
4773 }
4774 /* FALLTHROUGH */
4775 case NCLOBBER:
4776 fname = redir->nfile.expfname;
4777 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4778 if (f < 0)
4779 goto ecreate;
4780 break;
4781 case NAPPEND:
4782 fname = redir->nfile.expfname;
4783 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4784 if (f < 0)
4785 goto ecreate;
4786 break;
4787 default:
4788#if DEBUG
4789 abort();
4790#endif
4791 /* Fall through to eliminate warning. */
4792 case NTOFD:
4793 case NFROMFD:
4794 f = -1;
4795 break;
4796 case NHERE:
4797 case NXHERE:
4798 f = openhere(redir);
4799 break;
4800 }
4801
4802 return f;
4803 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004804 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004805 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004806 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004807}
4808
4809/*
4810 * Copy a file descriptor to be >= to. Returns -1
4811 * if the source file descriptor is closed, EMPTY if there are no unused
4812 * file descriptors left.
4813 */
4814static int
4815copyfd(int from, int to)
4816{
4817 int newfd;
4818
4819 newfd = fcntl(from, F_DUPFD, to);
4820 if (newfd < 0) {
4821 if (errno == EMFILE)
4822 return EMPTY;
4823 ash_msg_and_raise_error("%d: %m", from);
4824 }
4825 return newfd;
4826}
4827
4828static void
4829dupredirect(union node *redir, int f)
4830{
4831 int fd = redir->nfile.fd;
4832
4833 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4834 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4835 copyfd(redir->ndup.dupfd, fd);
4836 }
4837 return;
4838 }
4839
4840 if (f != fd) {
4841 copyfd(f, fd);
4842 close(f);
4843 }
4844}
4845
4846/*
4847 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4848 * old file descriptors are stashed away so that the redirection can be
4849 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4850 * standard output, and the standard error if it becomes a duplicate of
4851 * stdout, is saved in memory.
4852 */
4853/* flags passed to redirect */
4854#define REDIR_PUSH 01 /* save previous values of file descriptors */
4855#define REDIR_SAVEFD2 03 /* set preverrout */
4856static void
4857redirect(union node *redir, int flags)
4858{
4859 union node *n;
4860 struct redirtab *sv;
4861 int i;
4862 int fd;
4863 int newfd;
4864 int *p;
4865 nullredirs++;
4866 if (!redir) {
4867 return;
4868 }
4869 sv = NULL;
4870 INT_OFF;
4871 if (flags & REDIR_PUSH) {
4872 struct redirtab *q;
4873 q = ckmalloc(sizeof(struct redirtab));
4874 q->next = redirlist;
4875 redirlist = q;
4876 q->nullredirs = nullredirs - 1;
4877 for (i = 0; i < 10; i++)
4878 q->renamed[i] = EMPTY;
4879 nullredirs = 0;
4880 sv = q;
4881 }
4882 n = redir;
4883 do {
4884 fd = n->nfile.fd;
4885 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4886 && n->ndup.dupfd == fd)
4887 continue; /* redirect from/to same file descriptor */
4888
4889 newfd = openredirect(n);
4890 if (fd == newfd)
4891 continue;
4892 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4893 i = fcntl(fd, F_DUPFD, 10);
4894
4895 if (i == -1) {
4896 i = errno;
4897 if (i != EBADF) {
4898 close(newfd);
4899 errno = i;
4900 ash_msg_and_raise_error("%d: %m", fd);
4901 /* NOTREACHED */
4902 }
4903 } else {
4904 *p = i;
4905 close(fd);
4906 }
4907 } else {
4908 close(fd);
4909 }
4910 dupredirect(n, newfd);
4911 } while ((n = n->nfile.next));
4912 INT_ON;
4913 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4914 preverrout_fd = sv->renamed[2];
4915}
4916
4917/*
4918 * Undo the effects of the last redirection.
4919 */
4920static void
4921popredir(int drop)
4922{
4923 struct redirtab *rp;
4924 int i;
4925
4926 if (--nullredirs >= 0)
4927 return;
4928 INT_OFF;
4929 rp = redirlist;
4930 for (i = 0; i < 10; i++) {
4931 if (rp->renamed[i] != EMPTY) {
4932 if (!drop) {
4933 close(i);
4934 copyfd(rp->renamed[i], i);
4935 }
4936 close(rp->renamed[i]);
4937 }
4938 }
4939 redirlist = rp->next;
4940 nullredirs = rp->nullredirs;
4941 free(rp);
4942 INT_ON;
4943}
4944
4945/*
4946 * Undo all redirections. Called on error or interrupt.
4947 */
4948
4949/*
4950 * Discard all saved file descriptors.
4951 */
4952static void
4953clearredir(int drop)
4954{
4955 for (;;) {
4956 nullredirs = 0;
4957 if (!redirlist)
4958 break;
4959 popredir(drop);
4960 }
4961}
4962
4963static int
4964redirectsafe(union node *redir, int flags)
4965{
4966 int err;
4967 volatile int saveint;
4968 struct jmploc *volatile savehandler = exception_handler;
4969 struct jmploc jmploc;
4970
4971 SAVE_INT(saveint);
4972 err = setjmp(jmploc.loc) * 2;
4973 if (!err) {
4974 exception_handler = &jmploc;
4975 redirect(redir, flags);
4976 }
4977 exception_handler = savehandler;
4978 if (err && exception != EXERROR)
4979 longjmp(exception_handler->loc, 1);
4980 RESTORE_INT(saveint);
4981 return err;
4982}
4983
4984
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004985/* ============ Routines to expand arguments to commands
4986 *
4987 * We have to deal with backquotes, shell variables, and file metacharacters.
4988 */
4989
4990/*
4991 * expandarg flags
4992 */
4993#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4994#define EXP_TILDE 0x2 /* do normal tilde expansion */
4995#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4996#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4997#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4998#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4999#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5000#define EXP_WORD 0x80 /* expand word in parameter expansion */
5001#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5002/*
5003 * _rmescape() flags
5004 */
5005#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5006#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5007#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5008#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5009#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5010
5011/*
5012 * Structure specifying which parts of the string should be searched
5013 * for IFS characters.
5014 */
5015struct ifsregion {
5016 struct ifsregion *next; /* next region in list */
5017 int begoff; /* offset of start of region */
5018 int endoff; /* offset of end of region */
5019 int nulonly; /* search for nul bytes only */
5020};
5021
5022struct arglist {
5023 struct strlist *list;
5024 struct strlist **lastp;
5025};
5026
5027/* output of current string */
5028static char *expdest;
5029/* list of back quote expressions */
5030static struct nodelist *argbackq;
5031/* first struct in list of ifs regions */
5032static struct ifsregion ifsfirst;
5033/* last struct in list */
5034static struct ifsregion *ifslastp;
5035/* holds expanded arg list */
5036static struct arglist exparg;
5037
5038/*
5039 * Our own itoa().
5040 */
5041static int
5042cvtnum(arith_t num)
5043{
5044 int len;
5045
5046 expdest = makestrspace(32, expdest);
5047#if ENABLE_ASH_MATH_SUPPORT_64
5048 len = fmtstr(expdest, 32, "%lld", (long long) num);
5049#else
5050 len = fmtstr(expdest, 32, "%ld", num);
5051#endif
5052 STADJUST(len, expdest);
5053 return len;
5054}
5055
5056static size_t
5057esclen(const char *start, const char *p)
5058{
5059 size_t esc = 0;
5060
5061 while (p > start && *--p == CTLESC) {
5062 esc++;
5063 }
5064 return esc;
5065}
5066
5067/*
5068 * Remove any CTLESC characters from a string.
5069 */
5070static char *
5071_rmescapes(char *str, int flag)
5072{
5073 char *p, *q, *r;
5074 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5075 unsigned inquotes;
5076 int notescaped;
5077 int globbing;
5078
5079 p = strpbrk(str, qchars);
5080 if (!p) {
5081 return str;
5082 }
5083 q = p;
5084 r = str;
5085 if (flag & RMESCAPE_ALLOC) {
5086 size_t len = p - str;
5087 size_t fulllen = len + strlen(p) + 1;
5088
5089 if (flag & RMESCAPE_GROW) {
5090 r = makestrspace(fulllen, expdest);
5091 } else if (flag & RMESCAPE_HEAP) {
5092 r = ckmalloc(fulllen);
5093 } else {
5094 r = stalloc(fulllen);
5095 }
5096 q = r;
5097 if (len > 0) {
5098 q = memcpy(q, str, len) + len;
5099 }
5100 }
5101 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5102 globbing = flag & RMESCAPE_GLOB;
5103 notescaped = globbing;
5104 while (*p) {
5105 if (*p == CTLQUOTEMARK) {
5106 inquotes = ~inquotes;
5107 p++;
5108 notescaped = globbing;
5109 continue;
5110 }
5111 if (*p == '\\') {
5112 /* naked back slash */
5113 notescaped = 0;
5114 goto copy;
5115 }
5116 if (*p == CTLESC) {
5117 p++;
5118 if (notescaped && inquotes && *p != '/') {
5119 *q++ = '\\';
5120 }
5121 }
5122 notescaped = globbing;
5123 copy:
5124 *q++ = *p++;
5125 }
5126 *q = '\0';
5127 if (flag & RMESCAPE_GROW) {
5128 expdest = r;
5129 STADJUST(q - r + 1, expdest);
5130 }
5131 return r;
5132}
5133#define rmescapes(p) _rmescapes((p), 0)
5134
5135#define pmatch(a, b) !fnmatch((a), (b), 0)
5136
5137/*
5138 * Prepare a pattern for a expmeta (internal glob(3)) call.
5139 *
5140 * Returns an stalloced string.
5141 */
5142static char *
5143preglob(const char *pattern, int quoted, int flag)
5144{
5145 flag |= RMESCAPE_GLOB;
5146 if (quoted) {
5147 flag |= RMESCAPE_QUOTED;
5148 }
5149 return _rmescapes((char *)pattern, flag);
5150}
5151
5152/*
5153 * Put a string on the stack.
5154 */
5155static void
5156memtodest(const char *p, size_t len, int syntax, int quotes)
5157{
5158 char *q = expdest;
5159
5160 q = makestrspace(len * 2, q);
5161
5162 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005163 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005164 if (!c)
5165 continue;
5166 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5167 USTPUTC(CTLESC, q);
5168 USTPUTC(c, q);
5169 }
5170
5171 expdest = q;
5172}
5173
5174static void
5175strtodest(const char *p, int syntax, int quotes)
5176{
5177 memtodest(p, strlen(p), syntax, quotes);
5178}
5179
5180/*
5181 * Record the fact that we have to scan this region of the
5182 * string for IFS characters.
5183 */
5184static void
5185recordregion(int start, int end, int nulonly)
5186{
5187 struct ifsregion *ifsp;
5188
5189 if (ifslastp == NULL) {
5190 ifsp = &ifsfirst;
5191 } else {
5192 INT_OFF;
5193 ifsp = ckmalloc(sizeof(*ifsp));
5194 ifsp->next = NULL;
5195 ifslastp->next = ifsp;
5196 INT_ON;
5197 }
5198 ifslastp = ifsp;
5199 ifslastp->begoff = start;
5200 ifslastp->endoff = end;
5201 ifslastp->nulonly = nulonly;
5202}
5203
5204static void
5205removerecordregions(int endoff)
5206{
5207 if (ifslastp == NULL)
5208 return;
5209
5210 if (ifsfirst.endoff > endoff) {
5211 while (ifsfirst.next != NULL) {
5212 struct ifsregion *ifsp;
5213 INT_OFF;
5214 ifsp = ifsfirst.next->next;
5215 free(ifsfirst.next);
5216 ifsfirst.next = ifsp;
5217 INT_ON;
5218 }
5219 if (ifsfirst.begoff > endoff)
5220 ifslastp = NULL;
5221 else {
5222 ifslastp = &ifsfirst;
5223 ifsfirst.endoff = endoff;
5224 }
5225 return;
5226 }
5227
5228 ifslastp = &ifsfirst;
5229 while (ifslastp->next && ifslastp->next->begoff < endoff)
5230 ifslastp=ifslastp->next;
5231 while (ifslastp->next != NULL) {
5232 struct ifsregion *ifsp;
5233 INT_OFF;
5234 ifsp = ifslastp->next->next;
5235 free(ifslastp->next);
5236 ifslastp->next = ifsp;
5237 INT_ON;
5238 }
5239 if (ifslastp->endoff > endoff)
5240 ifslastp->endoff = endoff;
5241}
5242
5243static char *
5244exptilde(char *startp, char *p, int flag)
5245{
5246 char c;
5247 char *name;
5248 struct passwd *pw;
5249 const char *home;
5250 int quotes = flag & (EXP_FULL | EXP_CASE);
5251 int startloc;
5252
5253 name = p + 1;
5254
5255 while ((c = *++p) != '\0') {
5256 switch (c) {
5257 case CTLESC:
5258 return startp;
5259 case CTLQUOTEMARK:
5260 return startp;
5261 case ':':
5262 if (flag & EXP_VARTILDE)
5263 goto done;
5264 break;
5265 case '/':
5266 case CTLENDVAR:
5267 goto done;
5268 }
5269 }
5270 done:
5271 *p = '\0';
5272 if (*name == '\0') {
5273 home = lookupvar(homestr);
5274 } else {
5275 pw = getpwnam(name);
5276 if (pw == NULL)
5277 goto lose;
5278 home = pw->pw_dir;
5279 }
5280 if (!home || !*home)
5281 goto lose;
5282 *p = c;
5283 startloc = expdest - (char *)stackblock();
5284 strtodest(home, SQSYNTAX, quotes);
5285 recordregion(startloc, expdest - (char *)stackblock(), 0);
5286 return p;
5287 lose:
5288 *p = c;
5289 return startp;
5290}
5291
5292/*
5293 * Execute a command inside back quotes. If it's a builtin command, we
5294 * want to save its output in a block obtained from malloc. Otherwise
5295 * we fork off a subprocess and get the output of the command via a pipe.
5296 * Should be called with interrupts off.
5297 */
5298struct backcmd { /* result of evalbackcmd */
5299 int fd; /* file descriptor to read from */
5300 char *buf; /* buffer */
5301 int nleft; /* number of chars in buffer */
5302 struct job *jp; /* job structure for command */
5303};
5304
5305/* These forward decls are needed to use "eval" code for backticks handling: */
5306static int back_exitstatus; /* exit status of backquoted command */
5307#define EV_EXIT 01 /* exit after evaluating tree */
5308static void evaltree(union node *, int);
5309
5310static void
5311evalbackcmd(union node *n, struct backcmd *result)
5312{
5313 int saveherefd;
5314
5315 result->fd = -1;
5316 result->buf = NULL;
5317 result->nleft = 0;
5318 result->jp = NULL;
5319 if (n == NULL) {
5320 goto out;
5321 }
5322
5323 saveherefd = herefd;
5324 herefd = -1;
5325
5326 {
5327 int pip[2];
5328 struct job *jp;
5329
5330 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005331 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005332 jp = makejob(n, 1);
5333 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5334 FORCE_INT_ON;
5335 close(pip[0]);
5336 if (pip[1] != 1) {
5337 close(1);
5338 copyfd(pip[1], 1);
5339 close(pip[1]);
5340 }
5341 eflag = 0;
5342 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5343 /* NOTREACHED */
5344 }
5345 close(pip[1]);
5346 result->fd = pip[0];
5347 result->jp = jp;
5348 }
5349 herefd = saveherefd;
5350 out:
5351 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5352 result->fd, result->buf, result->nleft, result->jp));
5353}
5354
5355/*
5356 * Expand stuff in backwards quotes.
5357 */
5358static void
5359expbackq(union node *cmd, int quoted, int quotes)
5360{
5361 struct backcmd in;
5362 int i;
5363 char buf[128];
5364 char *p;
5365 char *dest;
5366 int startloc;
5367 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5368 struct stackmark smark;
5369
5370 INT_OFF;
5371 setstackmark(&smark);
5372 dest = expdest;
5373 startloc = dest - (char *)stackblock();
5374 grabstackstr(dest);
5375 evalbackcmd(cmd, &in);
5376 popstackmark(&smark);
5377
5378 p = in.buf;
5379 i = in.nleft;
5380 if (i == 0)
5381 goto read;
5382 for (;;) {
5383 memtodest(p, i, syntax, quotes);
5384 read:
5385 if (in.fd < 0)
5386 break;
5387 i = safe_read(in.fd, buf, sizeof(buf));
5388 TRACE(("expbackq: read returns %d\n", i));
5389 if (i <= 0)
5390 break;
5391 p = buf;
5392 }
5393
5394 if (in.buf)
5395 free(in.buf);
5396 if (in.fd >= 0) {
5397 close(in.fd);
5398 back_exitstatus = waitforjob(in.jp);
5399 }
5400 INT_ON;
5401
5402 /* Eat all trailing newlines */
5403 dest = expdest;
5404 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5405 STUNPUTC(dest);
5406 expdest = dest;
5407
5408 if (quoted == 0)
5409 recordregion(startloc, dest - (char *)stackblock(), 0);
5410 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5411 (dest - (char *)stackblock()) - startloc,
5412 (dest - (char *)stackblock()) - startloc,
5413 stackblock() + startloc));
5414}
5415
5416#if ENABLE_ASH_MATH_SUPPORT
5417/*
5418 * Expand arithmetic expression. Backup to start of expression,
5419 * evaluate, place result in (backed up) result, adjust string position.
5420 */
5421static void
5422expari(int quotes)
5423{
5424 char *p, *start;
5425 int begoff;
5426 int flag;
5427 int len;
5428
5429 /* ifsfree(); */
5430
5431 /*
5432 * This routine is slightly over-complicated for
5433 * efficiency. Next we scan backwards looking for the
5434 * start of arithmetic.
5435 */
5436 start = stackblock();
5437 p = expdest - 1;
5438 *p = '\0';
5439 p--;
5440 do {
5441 int esc;
5442
5443 while (*p != CTLARI) {
5444 p--;
5445#if DEBUG
5446 if (p < start) {
5447 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5448 }
5449#endif
5450 }
5451
5452 esc = esclen(start, p);
5453 if (!(esc % 2)) {
5454 break;
5455 }
5456
5457 p -= esc + 1;
5458 } while (1);
5459
5460 begoff = p - start;
5461
5462 removerecordregions(begoff);
5463
5464 flag = p[1];
5465
5466 expdest = p;
5467
5468 if (quotes)
5469 rmescapes(p + 2);
5470
5471 len = cvtnum(dash_arith(p + 2));
5472
5473 if (flag != '"')
5474 recordregion(begoff, begoff + len, 0);
5475}
5476#endif
5477
5478/* argstr needs it */
5479static char *evalvar(char *p, int flag);
5480
5481/*
5482 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5483 * characters to allow for further processing. Otherwise treat
5484 * $@ like $* since no splitting will be performed.
5485 */
5486static void
5487argstr(char *p, int flag)
5488{
5489 static const char spclchars[] = {
5490 '=',
5491 ':',
5492 CTLQUOTEMARK,
5493 CTLENDVAR,
5494 CTLESC,
5495 CTLVAR,
5496 CTLBACKQ,
5497 CTLBACKQ | CTLQUOTE,
5498#if ENABLE_ASH_MATH_SUPPORT
5499 CTLENDARI,
5500#endif
5501 0
5502 };
5503 const char *reject = spclchars;
5504 int c;
5505 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5506 int breakall = flag & EXP_WORD;
5507 int inquotes;
5508 size_t length;
5509 int startloc;
5510
5511 if (!(flag & EXP_VARTILDE)) {
5512 reject += 2;
5513 } else if (flag & EXP_VARTILDE2) {
5514 reject++;
5515 }
5516 inquotes = 0;
5517 length = 0;
5518 if (flag & EXP_TILDE) {
5519 char *q;
5520
5521 flag &= ~EXP_TILDE;
5522 tilde:
5523 q = p;
5524 if (*q == CTLESC && (flag & EXP_QWORD))
5525 q++;
5526 if (*q == '~')
5527 p = exptilde(p, q, flag);
5528 }
5529 start:
5530 startloc = expdest - (char *)stackblock();
5531 for (;;) {
5532 length += strcspn(p + length, reject);
5533 c = p[length];
5534 if (c && (!(c & 0x80)
5535#if ENABLE_ASH_MATH_SUPPORT
5536 || c == CTLENDARI
5537#endif
5538 )) {
5539 /* c == '=' || c == ':' || c == CTLENDARI */
5540 length++;
5541 }
5542 if (length > 0) {
5543 int newloc;
5544 expdest = stack_nputstr(p, length, expdest);
5545 newloc = expdest - (char *)stackblock();
5546 if (breakall && !inquotes && newloc > startloc) {
5547 recordregion(startloc, newloc, 0);
5548 }
5549 startloc = newloc;
5550 }
5551 p += length + 1;
5552 length = 0;
5553
5554 switch (c) {
5555 case '\0':
5556 goto breakloop;
5557 case '=':
5558 if (flag & EXP_VARTILDE2) {
5559 p--;
5560 continue;
5561 }
5562 flag |= EXP_VARTILDE2;
5563 reject++;
5564 /* fall through */
5565 case ':':
5566 /*
5567 * sort of a hack - expand tildes in variable
5568 * assignments (after the first '=' and after ':'s).
5569 */
5570 if (*--p == '~') {
5571 goto tilde;
5572 }
5573 continue;
5574 }
5575
5576 switch (c) {
5577 case CTLENDVAR: /* ??? */
5578 goto breakloop;
5579 case CTLQUOTEMARK:
5580 /* "$@" syntax adherence hack */
5581 if (
5582 !inquotes &&
5583 !memcmp(p, dolatstr, 4) &&
5584 (p[4] == CTLQUOTEMARK || (
5585 p[4] == CTLENDVAR &&
5586 p[5] == CTLQUOTEMARK
5587 ))
5588 ) {
5589 p = evalvar(p + 1, flag) + 1;
5590 goto start;
5591 }
5592 inquotes = !inquotes;
5593 addquote:
5594 if (quotes) {
5595 p--;
5596 length++;
5597 startloc++;
5598 }
5599 break;
5600 case CTLESC:
5601 startloc++;
5602 length++;
5603 goto addquote;
5604 case CTLVAR:
5605 p = evalvar(p, flag);
5606 goto start;
5607 case CTLBACKQ:
5608 c = 0;
5609 case CTLBACKQ|CTLQUOTE:
5610 expbackq(argbackq->n, c, quotes);
5611 argbackq = argbackq->next;
5612 goto start;
5613#if ENABLE_ASH_MATH_SUPPORT
5614 case CTLENDARI:
5615 p--;
5616 expari(quotes);
5617 goto start;
5618#endif
5619 }
5620 }
5621 breakloop:
5622 ;
5623}
5624
5625static char *
5626scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5627 int zero)
5628{
5629 char *loc;
5630 char *loc2;
5631 char c;
5632
5633 loc = startp;
5634 loc2 = rmesc;
5635 do {
5636 int match;
5637 const char *s = loc2;
5638 c = *loc2;
5639 if (zero) {
5640 *loc2 = '\0';
5641 s = rmesc;
5642 }
5643 match = pmatch(str, s);
5644 *loc2 = c;
5645 if (match)
5646 return loc;
5647 if (quotes && *loc == CTLESC)
5648 loc++;
5649 loc++;
5650 loc2++;
5651 } while (c);
5652 return 0;
5653}
5654
5655static char *
5656scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5657 int zero)
5658{
5659 int esc = 0;
5660 char *loc;
5661 char *loc2;
5662
5663 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5664 int match;
5665 char c = *loc2;
5666 const char *s = loc2;
5667 if (zero) {
5668 *loc2 = '\0';
5669 s = rmesc;
5670 }
5671 match = pmatch(str, s);
5672 *loc2 = c;
5673 if (match)
5674 return loc;
5675 loc--;
5676 if (quotes) {
5677 if (--esc < 0) {
5678 esc = esclen(startp, loc);
5679 }
5680 if (esc % 2) {
5681 esc--;
5682 loc--;
5683 }
5684 }
5685 }
5686 return 0;
5687}
5688
5689static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5690static void
5691varunset(const char *end, const char *var, const char *umsg, int varflags)
5692{
5693 const char *msg;
5694 const char *tail;
5695
5696 tail = nullstr;
5697 msg = "parameter not set";
5698 if (umsg) {
5699 if (*end == CTLENDVAR) {
5700 if (varflags & VSNUL)
5701 tail = " or null";
5702 } else
5703 msg = umsg;
5704 }
5705 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5706}
5707
5708static const char *
5709subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5710{
5711 char *startp;
5712 char *loc;
5713 int saveherefd = herefd;
5714 struct nodelist *saveargbackq = argbackq;
5715 int amount;
5716 char *rmesc, *rmescend;
5717 int zero;
5718 char *(*scan)(char *, char *, char *, char *, int , int);
5719
5720 herefd = -1;
5721 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5722 STPUTC('\0', expdest);
5723 herefd = saveherefd;
5724 argbackq = saveargbackq;
5725 startp = stackblock() + startloc;
5726
5727 switch (subtype) {
5728 case VSASSIGN:
5729 setvar(str, startp, 0);
5730 amount = startp - expdest;
5731 STADJUST(amount, expdest);
5732 return startp;
5733
5734 case VSQUESTION:
5735 varunset(p, str, startp, varflags);
5736 /* NOTREACHED */
5737 }
5738
5739 subtype -= VSTRIMRIGHT;
5740#if DEBUG
5741 if (subtype < 0 || subtype > 3)
5742 abort();
5743#endif
5744
5745 rmesc = startp;
5746 rmescend = stackblock() + strloc;
5747 if (quotes) {
5748 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5749 if (rmesc != startp) {
5750 rmescend = expdest;
5751 startp = stackblock() + startloc;
5752 }
5753 }
5754 rmescend--;
5755 str = stackblock() + strloc;
5756 preglob(str, varflags & VSQUOTE, 0);
5757
5758 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5759 zero = subtype >> 1;
5760 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5761 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5762
5763 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5764 if (loc) {
5765 if (zero) {
5766 memmove(startp, loc, str - loc);
5767 loc = startp + (str - loc) - 1;
5768 }
5769 *loc = '\0';
5770 amount = loc - expdest;
5771 STADJUST(amount, expdest);
5772 }
5773 return loc;
5774}
5775
5776/*
5777 * Add the value of a specialized variable to the stack string.
5778 */
5779static ssize_t
5780varvalue(char *name, int varflags, int flags)
5781{
5782 int num;
5783 char *p;
5784 int i;
5785 int sep = 0;
5786 int sepq = 0;
5787 ssize_t len = 0;
5788 char **ap;
5789 int syntax;
5790 int quoted = varflags & VSQUOTE;
5791 int subtype = varflags & VSTYPE;
5792 int quotes = flags & (EXP_FULL | EXP_CASE);
5793
5794 if (quoted && (flags & EXP_FULL))
5795 sep = 1 << CHAR_BIT;
5796
5797 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5798 switch (*name) {
5799 case '$':
5800 num = rootpid;
5801 goto numvar;
5802 case '?':
5803 num = exitstatus;
5804 goto numvar;
5805 case '#':
5806 num = shellparam.nparam;
5807 goto numvar;
5808 case '!':
5809 num = backgndpid;
5810 if (num == 0)
5811 return -1;
5812 numvar:
5813 len = cvtnum(num);
5814 break;
5815 case '-':
5816 p = makestrspace(NOPTS, expdest);
5817 for (i = NOPTS - 1; i >= 0; i--) {
5818 if (optlist[i]) {
5819 USTPUTC(optletters(i), p);
5820 len++;
5821 }
5822 }
5823 expdest = p;
5824 break;
5825 case '@':
5826 if (sep)
5827 goto param;
5828 /* fall through */
5829 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005830 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005831 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5832 sepq = 1;
5833 param:
5834 ap = shellparam.p;
5835 if (!ap)
5836 return -1;
5837 while ((p = *ap++)) {
5838 size_t partlen;
5839
5840 partlen = strlen(p);
5841 len += partlen;
5842
5843 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5844 memtodest(p, partlen, syntax, quotes);
5845
5846 if (*ap && sep) {
5847 char *q;
5848
5849 len++;
5850 if (subtype == VSPLUS || subtype == VSLENGTH) {
5851 continue;
5852 }
5853 q = expdest;
5854 if (sepq)
5855 STPUTC(CTLESC, q);
5856 STPUTC(sep, q);
5857 expdest = q;
5858 }
5859 }
5860 return len;
5861 case '0':
5862 case '1':
5863 case '2':
5864 case '3':
5865 case '4':
5866 case '5':
5867 case '6':
5868 case '7':
5869 case '8':
5870 case '9':
5871 num = atoi(name);
5872 if (num < 0 || num > shellparam.nparam)
5873 return -1;
5874 p = num ? shellparam.p[num - 1] : arg0;
5875 goto value;
5876 default:
5877 p = lookupvar(name);
5878 value:
5879 if (!p)
5880 return -1;
5881
5882 len = strlen(p);
5883 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5884 memtodest(p, len, syntax, quotes);
5885 return len;
5886 }
5887
5888 if (subtype == VSPLUS || subtype == VSLENGTH)
5889 STADJUST(-len, expdest);
5890 return len;
5891}
5892
5893/*
5894 * Expand a variable, and return a pointer to the next character in the
5895 * input string.
5896 */
5897static char *
5898evalvar(char *p, int flag)
5899{
5900 int subtype;
5901 int varflags;
5902 char *var;
5903 int patloc;
5904 int c;
5905 int startloc;
5906 ssize_t varlen;
5907 int easy;
5908 int quotes;
5909 int quoted;
5910
5911 quotes = flag & (EXP_FULL | EXP_CASE);
5912 varflags = *p++;
5913 subtype = varflags & VSTYPE;
5914 quoted = varflags & VSQUOTE;
5915 var = p;
5916 easy = (!quoted || (*var == '@' && shellparam.nparam));
5917 startloc = expdest - (char *)stackblock();
5918 p = strchr(p, '=') + 1;
5919
5920 again:
5921 varlen = varvalue(var, varflags, flag);
5922 if (varflags & VSNUL)
5923 varlen--;
5924
5925 if (subtype == VSPLUS) {
5926 varlen = -1 - varlen;
5927 goto vsplus;
5928 }
5929
5930 if (subtype == VSMINUS) {
5931 vsplus:
5932 if (varlen < 0) {
5933 argstr(
5934 p, flag | EXP_TILDE |
5935 (quoted ? EXP_QWORD : EXP_WORD)
5936 );
5937 goto end;
5938 }
5939 if (easy)
5940 goto record;
5941 goto end;
5942 }
5943
5944 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5945 if (varlen < 0) {
5946 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5947 varflags &= ~VSNUL;
5948 /*
5949 * Remove any recorded regions beyond
5950 * start of variable
5951 */
5952 removerecordregions(startloc);
5953 goto again;
5954 }
5955 goto end;
5956 }
5957 if (easy)
5958 goto record;
5959 goto end;
5960 }
5961
5962 if (varlen < 0 && uflag)
5963 varunset(p, var, 0, 0);
5964
5965 if (subtype == VSLENGTH) {
5966 cvtnum(varlen > 0 ? varlen : 0);
5967 goto record;
5968 }
5969
5970 if (subtype == VSNORMAL) {
5971 if (!easy)
5972 goto end;
5973 record:
5974 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5975 goto end;
5976 }
5977
5978#if DEBUG
5979 switch (subtype) {
5980 case VSTRIMLEFT:
5981 case VSTRIMLEFTMAX:
5982 case VSTRIMRIGHT:
5983 case VSTRIMRIGHTMAX:
5984 break;
5985 default:
5986 abort();
5987 }
5988#endif
5989
5990 if (varlen >= 0) {
5991 /*
5992 * Terminate the string and start recording the pattern
5993 * right after it
5994 */
5995 STPUTC('\0', expdest);
5996 patloc = expdest - (char *)stackblock();
5997 if (subevalvar(p, NULL, patloc, subtype,
5998 startloc, varflags, quotes) == 0) {
5999 int amount = expdest - (
6000 (char *)stackblock() + patloc - 1
6001 );
6002 STADJUST(-amount, expdest);
6003 }
6004 /* Remove any recorded regions beyond start of variable */
6005 removerecordregions(startloc);
6006 goto record;
6007 }
6008
6009 end:
6010 if (subtype != VSNORMAL) { /* skip to end of alternative */
6011 int nesting = 1;
6012 for (;;) {
6013 c = *p++;
6014 if (c == CTLESC)
6015 p++;
6016 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6017 if (varlen >= 0)
6018 argbackq = argbackq->next;
6019 } else if (c == CTLVAR) {
6020 if ((*p++ & VSTYPE) != VSNORMAL)
6021 nesting++;
6022 } else if (c == CTLENDVAR) {
6023 if (--nesting == 0)
6024 break;
6025 }
6026 }
6027 }
6028 return p;
6029}
6030
6031/*
6032 * Break the argument string into pieces based upon IFS and add the
6033 * strings to the argument list. The regions of the string to be
6034 * searched for IFS characters have been stored by recordregion.
6035 */
6036static void
6037ifsbreakup(char *string, struct arglist *arglist)
6038{
6039 struct ifsregion *ifsp;
6040 struct strlist *sp;
6041 char *start;
6042 char *p;
6043 char *q;
6044 const char *ifs, *realifs;
6045 int ifsspc;
6046 int nulonly;
6047
6048 start = string;
6049 if (ifslastp != NULL) {
6050 ifsspc = 0;
6051 nulonly = 0;
6052 realifs = ifsset() ? ifsval() : defifs;
6053 ifsp = &ifsfirst;
6054 do {
6055 p = string + ifsp->begoff;
6056 nulonly = ifsp->nulonly;
6057 ifs = nulonly ? nullstr : realifs;
6058 ifsspc = 0;
6059 while (p < string + ifsp->endoff) {
6060 q = p;
6061 if (*p == CTLESC)
6062 p++;
6063 if (!strchr(ifs, *p)) {
6064 p++;
6065 continue;
6066 }
6067 if (!nulonly)
6068 ifsspc = (strchr(defifs, *p) != NULL);
6069 /* Ignore IFS whitespace at start */
6070 if (q == start && ifsspc) {
6071 p++;
6072 start = p;
6073 continue;
6074 }
6075 *q = '\0';
6076 sp = stalloc(sizeof(*sp));
6077 sp->text = start;
6078 *arglist->lastp = sp;
6079 arglist->lastp = &sp->next;
6080 p++;
6081 if (!nulonly) {
6082 for (;;) {
6083 if (p >= string + ifsp->endoff) {
6084 break;
6085 }
6086 q = p;
6087 if (*p == CTLESC)
6088 p++;
6089 if (strchr(ifs, *p) == NULL ) {
6090 p = q;
6091 break;
6092 } else if (strchr(defifs, *p) == NULL) {
6093 if (ifsspc) {
6094 p++;
6095 ifsspc = 0;
6096 } else {
6097 p = q;
6098 break;
6099 }
6100 } else
6101 p++;
6102 }
6103 }
6104 start = p;
6105 } /* while */
6106 ifsp = ifsp->next;
6107 } while (ifsp != NULL);
6108 if (nulonly)
6109 goto add;
6110 }
6111
6112 if (!*start)
6113 return;
6114
6115 add:
6116 sp = stalloc(sizeof(*sp));
6117 sp->text = start;
6118 *arglist->lastp = sp;
6119 arglist->lastp = &sp->next;
6120}
6121
6122static void
6123ifsfree(void)
6124{
6125 struct ifsregion *p;
6126
6127 INT_OFF;
6128 p = ifsfirst.next;
6129 do {
6130 struct ifsregion *ifsp;
6131 ifsp = p->next;
6132 free(p);
6133 p = ifsp;
6134 } while (p);
6135 ifslastp = NULL;
6136 ifsfirst.next = NULL;
6137 INT_ON;
6138}
6139
6140/*
6141 * Add a file name to the list.
6142 */
6143static void
6144addfname(const char *name)
6145{
6146 struct strlist *sp;
6147
6148 sp = stalloc(sizeof(*sp));
6149 sp->text = ststrdup(name);
6150 *exparg.lastp = sp;
6151 exparg.lastp = &sp->next;
6152}
6153
6154static char *expdir;
6155
6156/*
6157 * Do metacharacter (i.e. *, ?, [...]) expansion.
6158 */
6159static void
6160expmeta(char *enddir, char *name)
6161{
6162 char *p;
6163 const char *cp;
6164 char *start;
6165 char *endname;
6166 int metaflag;
6167 struct stat statb;
6168 DIR *dirp;
6169 struct dirent *dp;
6170 int atend;
6171 int matchdot;
6172
6173 metaflag = 0;
6174 start = name;
6175 for (p = name; *p; p++) {
6176 if (*p == '*' || *p == '?')
6177 metaflag = 1;
6178 else if (*p == '[') {
6179 char *q = p + 1;
6180 if (*q == '!')
6181 q++;
6182 for (;;) {
6183 if (*q == '\\')
6184 q++;
6185 if (*q == '/' || *q == '\0')
6186 break;
6187 if (*++q == ']') {
6188 metaflag = 1;
6189 break;
6190 }
6191 }
6192 } else if (*p == '\\')
6193 p++;
6194 else if (*p == '/') {
6195 if (metaflag)
6196 goto out;
6197 start = p + 1;
6198 }
6199 }
6200 out:
6201 if (metaflag == 0) { /* we've reached the end of the file name */
6202 if (enddir != expdir)
6203 metaflag++;
6204 p = name;
6205 do {
6206 if (*p == '\\')
6207 p++;
6208 *enddir++ = *p;
6209 } while (*p++);
6210 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6211 addfname(expdir);
6212 return;
6213 }
6214 endname = p;
6215 if (name < start) {
6216 p = name;
6217 do {
6218 if (*p == '\\')
6219 p++;
6220 *enddir++ = *p++;
6221 } while (p < start);
6222 }
6223 if (enddir == expdir) {
6224 cp = ".";
6225 } else if (enddir == expdir + 1 && *expdir == '/') {
6226 cp = "/";
6227 } else {
6228 cp = expdir;
6229 enddir[-1] = '\0';
6230 }
6231 dirp = opendir(cp);
6232 if (dirp == NULL)
6233 return;
6234 if (enddir != expdir)
6235 enddir[-1] = '/';
6236 if (*endname == 0) {
6237 atend = 1;
6238 } else {
6239 atend = 0;
6240 *endname++ = '\0';
6241 }
6242 matchdot = 0;
6243 p = start;
6244 if (*p == '\\')
6245 p++;
6246 if (*p == '.')
6247 matchdot++;
6248 while (! intpending && (dp = readdir(dirp)) != NULL) {
6249 if (dp->d_name[0] == '.' && ! matchdot)
6250 continue;
6251 if (pmatch(start, dp->d_name)) {
6252 if (atend) {
6253 strcpy(enddir, dp->d_name);
6254 addfname(expdir);
6255 } else {
6256 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6257 continue;
6258 p[-1] = '/';
6259 expmeta(p, endname);
6260 }
6261 }
6262 }
6263 closedir(dirp);
6264 if (! atend)
6265 endname[-1] = '/';
6266}
6267
6268static struct strlist *
6269msort(struct strlist *list, int len)
6270{
6271 struct strlist *p, *q = NULL;
6272 struct strlist **lpp;
6273 int half;
6274 int n;
6275
6276 if (len <= 1)
6277 return list;
6278 half = len >> 1;
6279 p = list;
6280 for (n = half; --n >= 0; ) {
6281 q = p;
6282 p = p->next;
6283 }
6284 q->next = NULL; /* terminate first half of list */
6285 q = msort(list, half); /* sort first half of list */
6286 p = msort(p, len - half); /* sort second half */
6287 lpp = &list;
6288 for (;;) {
6289#if ENABLE_LOCALE_SUPPORT
6290 if (strcoll(p->text, q->text) < 0)
6291#else
6292 if (strcmp(p->text, q->text) < 0)
6293#endif
6294 {
6295 *lpp = p;
6296 lpp = &p->next;
6297 p = *lpp;
6298 if (p == NULL) {
6299 *lpp = q;
6300 break;
6301 }
6302 } else {
6303 *lpp = q;
6304 lpp = &q->next;
6305 q = *lpp;
6306 if (q == NULL) {
6307 *lpp = p;
6308 break;
6309 }
6310 }
6311 }
6312 return list;
6313}
6314
6315/*
6316 * Sort the results of file name expansion. It calculates the number of
6317 * strings to sort and then calls msort (short for merge sort) to do the
6318 * work.
6319 */
6320static struct strlist *
6321expsort(struct strlist *str)
6322{
6323 int len;
6324 struct strlist *sp;
6325
6326 len = 0;
6327 for (sp = str; sp; sp = sp->next)
6328 len++;
6329 return msort(str, len);
6330}
6331
6332static void
6333expandmeta(struct strlist *str, int flag)
6334{
6335 static const char metachars[] = {
6336 '*', '?', '[', 0
6337 };
6338 /* TODO - EXP_REDIR */
6339
6340 while (str) {
6341 struct strlist **savelastp;
6342 struct strlist *sp;
6343 char *p;
6344
6345 if (fflag)
6346 goto nometa;
6347 if (!strpbrk(str->text, metachars))
6348 goto nometa;
6349 savelastp = exparg.lastp;
6350
6351 INT_OFF;
6352 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6353 {
6354 int i = strlen(str->text);
6355 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6356 }
6357
6358 expmeta(expdir, p);
6359 free(expdir);
6360 if (p != str->text)
6361 free(p);
6362 INT_ON;
6363 if (exparg.lastp == savelastp) {
6364 /*
6365 * no matches
6366 */
6367 nometa:
6368 *exparg.lastp = str;
6369 rmescapes(str->text);
6370 exparg.lastp = &str->next;
6371 } else {
6372 *exparg.lastp = NULL;
6373 *savelastp = sp = expsort(*savelastp);
6374 while (sp->next != NULL)
6375 sp = sp->next;
6376 exparg.lastp = &sp->next;
6377 }
6378 str = str->next;
6379 }
6380}
6381
6382/*
6383 * Perform variable substitution and command substitution on an argument,
6384 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6385 * perform splitting and file name expansion. When arglist is NULL, perform
6386 * here document expansion.
6387 */
6388static void
6389expandarg(union node *arg, struct arglist *arglist, int flag)
6390{
6391 struct strlist *sp;
6392 char *p;
6393
6394 argbackq = arg->narg.backquote;
6395 STARTSTACKSTR(expdest);
6396 ifsfirst.next = NULL;
6397 ifslastp = NULL;
6398 argstr(arg->narg.text, flag);
6399 p = _STPUTC('\0', expdest);
6400 expdest = p - 1;
6401 if (arglist == NULL) {
6402 return; /* here document expanded */
6403 }
6404 p = grabstackstr(p);
6405 exparg.lastp = &exparg.list;
6406 /*
6407 * TODO - EXP_REDIR
6408 */
6409 if (flag & EXP_FULL) {
6410 ifsbreakup(p, &exparg);
6411 *exparg.lastp = NULL;
6412 exparg.lastp = &exparg.list;
6413 expandmeta(exparg.list, flag);
6414 } else {
6415 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6416 rmescapes(p);
6417 sp = stalloc(sizeof(*sp));
6418 sp->text = p;
6419 *exparg.lastp = sp;
6420 exparg.lastp = &sp->next;
6421 }
6422 if (ifsfirst.next)
6423 ifsfree();
6424 *exparg.lastp = NULL;
6425 if (exparg.list) {
6426 *arglist->lastp = exparg.list;
6427 arglist->lastp = exparg.lastp;
6428 }
6429}
6430
6431/*
6432 * Expand shell variables and backquotes inside a here document.
6433 */
6434static void
6435expandhere(union node *arg, int fd)
6436{
6437 herefd = fd;
6438 expandarg(arg, (struct arglist *)NULL, 0);
6439 full_write(fd, stackblock(), expdest - (char *)stackblock());
6440}
6441
6442/*
6443 * Returns true if the pattern matches the string.
6444 */
6445static int
6446patmatch(char *pattern, const char *string)
6447{
6448 return pmatch(preglob(pattern, 0, 0), string);
6449}
6450
6451/*
6452 * See if a pattern matches in a case statement.
6453 */
6454static int
6455casematch(union node *pattern, char *val)
6456{
6457 struct stackmark smark;
6458 int result;
6459
6460 setstackmark(&smark);
6461 argbackq = pattern->narg.backquote;
6462 STARTSTACKSTR(expdest);
6463 ifslastp = NULL;
6464 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6465 STACKSTRNUL(expdest);
6466 result = patmatch(stackblock(), val);
6467 popstackmark(&smark);
6468 return result;
6469}
6470
6471
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006472/* ============ find_command */
6473
6474struct builtincmd {
6475 const char *name;
6476 int (*builtin)(int, char **);
6477 /* unsigned flags; */
6478};
6479#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6480#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6481#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6482
6483struct cmdentry {
6484 int cmdtype;
6485 union param {
6486 int index;
6487 const struct builtincmd *cmd;
6488 struct funcnode *func;
6489 } u;
6490};
6491/* values of cmdtype */
6492#define CMDUNKNOWN -1 /* no entry in table for command */
6493#define CMDNORMAL 0 /* command is an executable program */
6494#define CMDFUNCTION 1 /* command is a shell function */
6495#define CMDBUILTIN 2 /* command is a shell builtin */
6496
6497/* action to find_command() */
6498#define DO_ERR 0x01 /* prints errors */
6499#define DO_ABS 0x02 /* checks absolute paths */
6500#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6501#define DO_ALTPATH 0x08 /* using alternate path */
6502#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6503
6504static void find_command(char *, struct cmdentry *, int, const char *);
6505
6506
6507/* ============ Hashing commands */
6508
6509/*
6510 * When commands are first encountered, they are entered in a hash table.
6511 * This ensures that a full path search will not have to be done for them
6512 * on each invocation.
6513 *
6514 * We should investigate converting to a linear search, even though that
6515 * would make the command name "hash" a misnomer.
6516 */
6517
6518#define CMDTABLESIZE 31 /* should be prime */
6519#define ARB 1 /* actual size determined at run time */
6520
6521struct tblentry {
6522 struct tblentry *next; /* next entry in hash chain */
6523 union param param; /* definition of builtin function */
6524 short cmdtype; /* index identifying command */
6525 char rehash; /* if set, cd done since entry created */
6526 char cmdname[ARB]; /* name of command */
6527};
6528
6529static struct tblentry *cmdtable[CMDTABLESIZE];
6530static int builtinloc = -1; /* index in path of %builtin, or -1 */
6531
6532static void
6533tryexec(char *cmd, char **argv, char **envp)
6534{
6535 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006536
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006537#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006538 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006539 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006540
6541 a = find_applet_by_name(cmd);
6542 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006543 if (a->noexec) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006544 current_applet = a;
Denis Vlasenkof5294e12007-04-14 10:09:57 +00006545 run_current_applet_and_exit(argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006546 }
6547 /* re-exec ourselves with the new arguments */
6548 execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
6549 /* If they called chroot or otherwise made the binary no longer
6550 * executable, fall through */
6551 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006552 }
6553#endif
6554
6555 repeat:
6556#ifdef SYSV
6557 do {
6558 execve(cmd, argv, envp);
6559 } while (errno == EINTR);
6560#else
6561 execve(cmd, argv, envp);
6562#endif
6563 if (repeated++) {
6564 free(argv);
6565 } else if (errno == ENOEXEC) {
6566 char **ap;
6567 char **new;
6568
6569 for (ap = argv; *ap; ap++)
6570 ;
6571 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6572 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006573 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006574 ap += 2;
6575 argv++;
6576 while ((*ap++ = *argv++))
6577 ;
6578 argv = new;
6579 goto repeat;
6580 }
6581}
6582
6583/*
6584 * Exec a program. Never returns. If you change this routine, you may
6585 * have to change the find_command routine as well.
6586 */
6587#define environment() listvars(VEXPORT, VUNSET, 0)
6588static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6589static void
6590shellexec(char **argv, const char *path, int idx)
6591{
6592 char *cmdname;
6593 int e;
6594 char **envp;
6595 int exerrno;
6596
6597 clearredir(1);
6598 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006599 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006600#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006601 || find_applet_by_name(argv[0])
6602#endif
6603 ) {
6604 tryexec(argv[0], argv, envp);
6605 e = errno;
6606 } else {
6607 e = ENOENT;
6608 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6609 if (--idx < 0 && pathopt == NULL) {
6610 tryexec(cmdname, argv, envp);
6611 if (errno != ENOENT && errno != ENOTDIR)
6612 e = errno;
6613 }
6614 stunalloc(cmdname);
6615 }
6616 }
6617
6618 /* Map to POSIX errors */
6619 switch (e) {
6620 case EACCES:
6621 exerrno = 126;
6622 break;
6623 case ENOENT:
6624 exerrno = 127;
6625 break;
6626 default:
6627 exerrno = 2;
6628 break;
6629 }
6630 exitstatus = exerrno;
6631 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6632 argv[0], e, suppressint ));
6633 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6634 /* NOTREACHED */
6635}
6636
6637static void
6638printentry(struct tblentry *cmdp)
6639{
6640 int idx;
6641 const char *path;
6642 char *name;
6643
6644 idx = cmdp->param.index;
6645 path = pathval();
6646 do {
6647 name = padvance(&path, cmdp->cmdname);
6648 stunalloc(name);
6649 } while (--idx >= 0);
6650 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6651}
6652
6653/*
6654 * Clear out command entries. The argument specifies the first entry in
6655 * PATH which has changed.
6656 */
6657static void
6658clearcmdentry(int firstchange)
6659{
6660 struct tblentry **tblp;
6661 struct tblentry **pp;
6662 struct tblentry *cmdp;
6663
6664 INT_OFF;
6665 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6666 pp = tblp;
6667 while ((cmdp = *pp) != NULL) {
6668 if ((cmdp->cmdtype == CMDNORMAL &&
6669 cmdp->param.index >= firstchange)
6670 || (cmdp->cmdtype == CMDBUILTIN &&
6671 builtinloc >= firstchange)
6672 ) {
6673 *pp = cmdp->next;
6674 free(cmdp);
6675 } else {
6676 pp = &cmdp->next;
6677 }
6678 }
6679 }
6680 INT_ON;
6681}
6682
6683/*
6684 * Locate a command in the command hash table. If "add" is nonzero,
6685 * add the command to the table if it is not already present. The
6686 * variable "lastcmdentry" is set to point to the address of the link
6687 * pointing to the entry, so that delete_cmd_entry can delete the
6688 * entry.
6689 *
6690 * Interrupts must be off if called with add != 0.
6691 */
6692static struct tblentry **lastcmdentry;
6693
6694static struct tblentry *
6695cmdlookup(const char *name, int add)
6696{
6697 unsigned int hashval;
6698 const char *p;
6699 struct tblentry *cmdp;
6700 struct tblentry **pp;
6701
6702 p = name;
6703 hashval = (unsigned char)*p << 4;
6704 while (*p)
6705 hashval += (unsigned char)*p++;
6706 hashval &= 0x7FFF;
6707 pp = &cmdtable[hashval % CMDTABLESIZE];
6708 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6709 if (strcmp(cmdp->cmdname, name) == 0)
6710 break;
6711 pp = &cmdp->next;
6712 }
6713 if (add && cmdp == NULL) {
6714 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6715 + strlen(name) + 1);
6716 cmdp->next = NULL;
6717 cmdp->cmdtype = CMDUNKNOWN;
6718 strcpy(cmdp->cmdname, name);
6719 }
6720 lastcmdentry = pp;
6721 return cmdp;
6722}
6723
6724/*
6725 * Delete the command entry returned on the last lookup.
6726 */
6727static void
6728delete_cmd_entry(void)
6729{
6730 struct tblentry *cmdp;
6731
6732 INT_OFF;
6733 cmdp = *lastcmdentry;
6734 *lastcmdentry = cmdp->next;
6735 if (cmdp->cmdtype == CMDFUNCTION)
6736 freefunc(cmdp->param.func);
6737 free(cmdp);
6738 INT_ON;
6739}
6740
6741/*
6742 * Add a new command entry, replacing any existing command entry for
6743 * the same name - except special builtins.
6744 */
6745static void
6746addcmdentry(char *name, struct cmdentry *entry)
6747{
6748 struct tblentry *cmdp;
6749
6750 cmdp = cmdlookup(name, 1);
6751 if (cmdp->cmdtype == CMDFUNCTION) {
6752 freefunc(cmdp->param.func);
6753 }
6754 cmdp->cmdtype = entry->cmdtype;
6755 cmdp->param = entry->u;
6756 cmdp->rehash = 0;
6757}
6758
6759static int
6760hashcmd(int argc, char **argv)
6761{
6762 struct tblentry **pp;
6763 struct tblentry *cmdp;
6764 int c;
6765 struct cmdentry entry;
6766 char *name;
6767
6768 while ((c = nextopt("r")) != '\0') {
6769 clearcmdentry(0);
6770 return 0;
6771 }
6772 if (*argptr == NULL) {
6773 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6774 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6775 if (cmdp->cmdtype == CMDNORMAL)
6776 printentry(cmdp);
6777 }
6778 }
6779 return 0;
6780 }
6781 c = 0;
6782 while ((name = *argptr) != NULL) {
6783 cmdp = cmdlookup(name, 0);
6784 if (cmdp != NULL
6785 && (cmdp->cmdtype == CMDNORMAL
6786 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6787 delete_cmd_entry();
6788 find_command(name, &entry, DO_ERR, pathval());
6789 if (entry.cmdtype == CMDUNKNOWN)
6790 c = 1;
6791 argptr++;
6792 }
6793 return c;
6794}
6795
6796/*
6797 * Called when a cd is done. Marks all commands so the next time they
6798 * are executed they will be rehashed.
6799 */
6800static void
6801hashcd(void)
6802{
6803 struct tblentry **pp;
6804 struct tblentry *cmdp;
6805
6806 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6807 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6808 if (cmdp->cmdtype == CMDNORMAL || (
6809 cmdp->cmdtype == CMDBUILTIN &&
6810 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6811 builtinloc > 0
6812 ))
6813 cmdp->rehash = 1;
6814 }
6815 }
6816}
6817
6818/*
6819 * Fix command hash table when PATH changed.
6820 * Called before PATH is changed. The argument is the new value of PATH;
6821 * pathval() still returns the old value at this point.
6822 * Called with interrupts off.
6823 */
6824static void
6825changepath(const char *newval)
6826{
6827 const char *old, *new;
6828 int idx;
6829 int firstchange;
6830 int idx_bltin;
6831
6832 old = pathval();
6833 new = newval;
6834 firstchange = 9999; /* assume no change */
6835 idx = 0;
6836 idx_bltin = -1;
6837 for (;;) {
6838 if (*old != *new) {
6839 firstchange = idx;
6840 if ((*old == '\0' && *new == ':')
6841 || (*old == ':' && *new == '\0'))
6842 firstchange++;
6843 old = new; /* ignore subsequent differences */
6844 }
6845 if (*new == '\0')
6846 break;
6847 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6848 idx_bltin = idx;
6849 if (*new == ':') {
6850 idx++;
6851 }
6852 new++, old++;
6853 }
6854 if (builtinloc < 0 && idx_bltin >= 0)
6855 builtinloc = idx_bltin; /* zap builtins */
6856 if (builtinloc >= 0 && idx_bltin < 0)
6857 firstchange = 0;
6858 clearcmdentry(firstchange);
6859 builtinloc = idx_bltin;
6860}
6861
6862#define TEOF 0
6863#define TNL 1
6864#define TREDIR 2
6865#define TWORD 3
6866#define TSEMI 4
6867#define TBACKGND 5
6868#define TAND 6
6869#define TOR 7
6870#define TPIPE 8
6871#define TLP 9
6872#define TRP 10
6873#define TENDCASE 11
6874#define TENDBQUOTE 12
6875#define TNOT 13
6876#define TCASE 14
6877#define TDO 15
6878#define TDONE 16
6879#define TELIF 17
6880#define TELSE 18
6881#define TESAC 19
6882#define TFI 20
6883#define TFOR 21
6884#define TIF 22
6885#define TIN 23
6886#define TTHEN 24
6887#define TUNTIL 25
6888#define TWHILE 26
6889#define TBEGIN 27
6890#define TEND 28
6891
6892/* first char is indicating which tokens mark the end of a list */
6893static const char *const tokname_array[] = {
6894 "\1end of file",
6895 "\0newline",
6896 "\0redirection",
6897 "\0word",
6898 "\0;",
6899 "\0&",
6900 "\0&&",
6901 "\0||",
6902 "\0|",
6903 "\0(",
6904 "\1)",
6905 "\1;;",
6906 "\1`",
6907#define KWDOFFSET 13
6908 /* the following are keywords */
6909 "\0!",
6910 "\0case",
6911 "\1do",
6912 "\1done",
6913 "\1elif",
6914 "\1else",
6915 "\1esac",
6916 "\1fi",
6917 "\0for",
6918 "\0if",
6919 "\0in",
6920 "\1then",
6921 "\0until",
6922 "\0while",
6923 "\0{",
6924 "\1}",
6925};
6926
6927static const char *
6928tokname(int tok)
6929{
6930 static char buf[16];
6931
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006932//try this:
6933//if (tok < TSEMI) return tokname_array[tok] + 1;
6934//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6935//return buf;
6936
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006937 if (tok >= TSEMI)
6938 buf[0] = '"';
6939 sprintf(buf + (tok >= TSEMI), "%s%c",
6940 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6941 return buf;
6942}
6943
6944/* Wrapper around strcmp for qsort/bsearch/... */
6945static int
6946pstrcmp(const void *a, const void *b)
6947{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006948 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006949}
6950
6951static const char *const *
6952findkwd(const char *s)
6953{
6954 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006955 (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
6956 sizeof(char *), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006957}
6958
6959/*
6960 * Locate and print what a word is...
6961 */
6962#if ENABLE_ASH_CMDCMD
6963static int
6964describe_command(char *command, int describe_command_verbose)
6965#else
6966#define describe_command_verbose 1
6967static int
6968describe_command(char *command)
6969#endif
6970{
6971 struct cmdentry entry;
6972 struct tblentry *cmdp;
6973#if ENABLE_ASH_ALIAS
6974 const struct alias *ap;
6975#endif
6976 const char *path = pathval();
6977
6978 if (describe_command_verbose) {
6979 out1str(command);
6980 }
6981
6982 /* First look at the keywords */
6983 if (findkwd(command)) {
6984 out1str(describe_command_verbose ? " is a shell keyword" : command);
6985 goto out;
6986 }
6987
6988#if ENABLE_ASH_ALIAS
6989 /* Then look at the aliases */
6990 ap = lookupalias(command, 0);
6991 if (ap != NULL) {
6992 if (describe_command_verbose) {
6993 out1fmt(" is an alias for %s", ap->val);
6994 } else {
6995 out1str("alias ");
6996 printalias(ap);
6997 return 0;
6998 }
6999 goto out;
7000 }
7001#endif
7002 /* Then check if it is a tracked alias */
7003 cmdp = cmdlookup(command, 0);
7004 if (cmdp != NULL) {
7005 entry.cmdtype = cmdp->cmdtype;
7006 entry.u = cmdp->param;
7007 } else {
7008 /* Finally use brute force */
7009 find_command(command, &entry, DO_ABS, path);
7010 }
7011
7012 switch (entry.cmdtype) {
7013 case CMDNORMAL: {
7014 int j = entry.u.index;
7015 char *p;
7016 if (j == -1) {
7017 p = command;
7018 } else {
7019 do {
7020 p = padvance(&path, command);
7021 stunalloc(p);
7022 } while (--j >= 0);
7023 }
7024 if (describe_command_verbose) {
7025 out1fmt(" is%s %s",
7026 (cmdp ? " a tracked alias for" : nullstr), p
7027 );
7028 } else {
7029 out1str(p);
7030 }
7031 break;
7032 }
7033
7034 case CMDFUNCTION:
7035 if (describe_command_verbose) {
7036 out1str(" is a shell function");
7037 } else {
7038 out1str(command);
7039 }
7040 break;
7041
7042 case CMDBUILTIN:
7043 if (describe_command_verbose) {
7044 out1fmt(" is a %sshell builtin",
7045 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7046 "special " : nullstr
7047 );
7048 } else {
7049 out1str(command);
7050 }
7051 break;
7052
7053 default:
7054 if (describe_command_verbose) {
7055 out1str(": not found\n");
7056 }
7057 return 127;
7058 }
7059 out:
7060 outstr("\n", stdout);
7061 return 0;
7062}
7063
7064static int
7065typecmd(int argc, char **argv)
7066{
7067 int i;
7068 int err = 0;
7069
7070 for (i = 1; i < argc; i++) {
7071#if ENABLE_ASH_CMDCMD
7072 err |= describe_command(argv[i], 1);
7073#else
7074 err |= describe_command(argv[i]);
7075#endif
7076 }
7077 return err;
7078}
7079
7080#if ENABLE_ASH_CMDCMD
7081static int
7082commandcmd(int argc, char **argv)
7083{
7084 int c;
7085 enum {
7086 VERIFY_BRIEF = 1,
7087 VERIFY_VERBOSE = 2,
7088 } verify = 0;
7089
7090 while ((c = nextopt("pvV")) != '\0')
7091 if (c == 'V')
7092 verify |= VERIFY_VERBOSE;
7093 else if (c == 'v')
7094 verify |= VERIFY_BRIEF;
7095#if DEBUG
7096 else if (c != 'p')
7097 abort();
7098#endif
7099 if (verify)
7100 return describe_command(*argptr, verify - VERIFY_BRIEF);
7101
7102 return 0;
7103}
7104#endif
7105
7106
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007107/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007108
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007109static int funcblocksize; /* size of structures in function */
7110static int funcstringsize; /* size of strings in node */
7111static void *funcblock; /* block to allocate function from */
7112static char *funcstring; /* block to allocate strings from */
7113
Eric Andersencb57d552001-06-28 07:25:16 +00007114/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007115#define EV_EXIT 01 /* exit after evaluating tree */
7116#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7117#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007118
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007119static const short nodesize[26] = {
7120 SHELL_ALIGN(sizeof(struct ncmd)),
7121 SHELL_ALIGN(sizeof(struct npipe)),
7122 SHELL_ALIGN(sizeof(struct nredir)),
7123 SHELL_ALIGN(sizeof(struct nredir)),
7124 SHELL_ALIGN(sizeof(struct nredir)),
7125 SHELL_ALIGN(sizeof(struct nbinary)),
7126 SHELL_ALIGN(sizeof(struct nbinary)),
7127 SHELL_ALIGN(sizeof(struct nbinary)),
7128 SHELL_ALIGN(sizeof(struct nif)),
7129 SHELL_ALIGN(sizeof(struct nbinary)),
7130 SHELL_ALIGN(sizeof(struct nbinary)),
7131 SHELL_ALIGN(sizeof(struct nfor)),
7132 SHELL_ALIGN(sizeof(struct ncase)),
7133 SHELL_ALIGN(sizeof(struct nclist)),
7134 SHELL_ALIGN(sizeof(struct narg)),
7135 SHELL_ALIGN(sizeof(struct narg)),
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 nfile)),
7141 SHELL_ALIGN(sizeof(struct ndup)),
7142 SHELL_ALIGN(sizeof(struct ndup)),
7143 SHELL_ALIGN(sizeof(struct nhere)),
7144 SHELL_ALIGN(sizeof(struct nhere)),
7145 SHELL_ALIGN(sizeof(struct nnot)),
7146};
7147
7148static void calcsize(union node *n);
7149
7150static void
7151sizenodelist(struct nodelist *lp)
7152{
7153 while (lp) {
7154 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7155 calcsize(lp->n);
7156 lp = lp->next;
7157 }
7158}
7159
7160static void
7161calcsize(union node *n)
7162{
7163 if (n == NULL)
7164 return;
7165 funcblocksize += nodesize[n->type];
7166 switch (n->type) {
7167 case NCMD:
7168 calcsize(n->ncmd.redirect);
7169 calcsize(n->ncmd.args);
7170 calcsize(n->ncmd.assign);
7171 break;
7172 case NPIPE:
7173 sizenodelist(n->npipe.cmdlist);
7174 break;
7175 case NREDIR:
7176 case NBACKGND:
7177 case NSUBSHELL:
7178 calcsize(n->nredir.redirect);
7179 calcsize(n->nredir.n);
7180 break;
7181 case NAND:
7182 case NOR:
7183 case NSEMI:
7184 case NWHILE:
7185 case NUNTIL:
7186 calcsize(n->nbinary.ch2);
7187 calcsize(n->nbinary.ch1);
7188 break;
7189 case NIF:
7190 calcsize(n->nif.elsepart);
7191 calcsize(n->nif.ifpart);
7192 calcsize(n->nif.test);
7193 break;
7194 case NFOR:
7195 funcstringsize += strlen(n->nfor.var) + 1;
7196 calcsize(n->nfor.body);
7197 calcsize(n->nfor.args);
7198 break;
7199 case NCASE:
7200 calcsize(n->ncase.cases);
7201 calcsize(n->ncase.expr);
7202 break;
7203 case NCLIST:
7204 calcsize(n->nclist.body);
7205 calcsize(n->nclist.pattern);
7206 calcsize(n->nclist.next);
7207 break;
7208 case NDEFUN:
7209 case NARG:
7210 sizenodelist(n->narg.backquote);
7211 funcstringsize += strlen(n->narg.text) + 1;
7212 calcsize(n->narg.next);
7213 break;
7214 case NTO:
7215 case NCLOBBER:
7216 case NFROM:
7217 case NFROMTO:
7218 case NAPPEND:
7219 calcsize(n->nfile.fname);
7220 calcsize(n->nfile.next);
7221 break;
7222 case NTOFD:
7223 case NFROMFD:
7224 calcsize(n->ndup.vname);
7225 calcsize(n->ndup.next);
7226 break;
7227 case NHERE:
7228 case NXHERE:
7229 calcsize(n->nhere.doc);
7230 calcsize(n->nhere.next);
7231 break;
7232 case NNOT:
7233 calcsize(n->nnot.com);
7234 break;
7235 };
7236}
7237
7238static char *
7239nodeckstrdup(char *s)
7240{
7241 char *rtn = funcstring;
7242
7243 strcpy(funcstring, s);
7244 funcstring += strlen(s) + 1;
7245 return rtn;
7246}
7247
7248static union node *copynode(union node *);
7249
7250static struct nodelist *
7251copynodelist(struct nodelist *lp)
7252{
7253 struct nodelist *start;
7254 struct nodelist **lpp;
7255
7256 lpp = &start;
7257 while (lp) {
7258 *lpp = funcblock;
7259 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7260 (*lpp)->n = copynode(lp->n);
7261 lp = lp->next;
7262 lpp = &(*lpp)->next;
7263 }
7264 *lpp = NULL;
7265 return start;
7266}
7267
7268static union node *
7269copynode(union node *n)
7270{
7271 union node *new;
7272
7273 if (n == NULL)
7274 return NULL;
7275 new = funcblock;
7276 funcblock = (char *) funcblock + nodesize[n->type];
7277
7278 switch (n->type) {
7279 case NCMD:
7280 new->ncmd.redirect = copynode(n->ncmd.redirect);
7281 new->ncmd.args = copynode(n->ncmd.args);
7282 new->ncmd.assign = copynode(n->ncmd.assign);
7283 break;
7284 case NPIPE:
7285 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7286 new->npipe.backgnd = n->npipe.backgnd;
7287 break;
7288 case NREDIR:
7289 case NBACKGND:
7290 case NSUBSHELL:
7291 new->nredir.redirect = copynode(n->nredir.redirect);
7292 new->nredir.n = copynode(n->nredir.n);
7293 break;
7294 case NAND:
7295 case NOR:
7296 case NSEMI:
7297 case NWHILE:
7298 case NUNTIL:
7299 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7300 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7301 break;
7302 case NIF:
7303 new->nif.elsepart = copynode(n->nif.elsepart);
7304 new->nif.ifpart = copynode(n->nif.ifpart);
7305 new->nif.test = copynode(n->nif.test);
7306 break;
7307 case NFOR:
7308 new->nfor.var = nodeckstrdup(n->nfor.var);
7309 new->nfor.body = copynode(n->nfor.body);
7310 new->nfor.args = copynode(n->nfor.args);
7311 break;
7312 case NCASE:
7313 new->ncase.cases = copynode(n->ncase.cases);
7314 new->ncase.expr = copynode(n->ncase.expr);
7315 break;
7316 case NCLIST:
7317 new->nclist.body = copynode(n->nclist.body);
7318 new->nclist.pattern = copynode(n->nclist.pattern);
7319 new->nclist.next = copynode(n->nclist.next);
7320 break;
7321 case NDEFUN:
7322 case NARG:
7323 new->narg.backquote = copynodelist(n->narg.backquote);
7324 new->narg.text = nodeckstrdup(n->narg.text);
7325 new->narg.next = copynode(n->narg.next);
7326 break;
7327 case NTO:
7328 case NCLOBBER:
7329 case NFROM:
7330 case NFROMTO:
7331 case NAPPEND:
7332 new->nfile.fname = copynode(n->nfile.fname);
7333 new->nfile.fd = n->nfile.fd;
7334 new->nfile.next = copynode(n->nfile.next);
7335 break;
7336 case NTOFD:
7337 case NFROMFD:
7338 new->ndup.vname = copynode(n->ndup.vname);
7339 new->ndup.dupfd = n->ndup.dupfd;
7340 new->ndup.fd = n->ndup.fd;
7341 new->ndup.next = copynode(n->ndup.next);
7342 break;
7343 case NHERE:
7344 case NXHERE:
7345 new->nhere.doc = copynode(n->nhere.doc);
7346 new->nhere.fd = n->nhere.fd;
7347 new->nhere.next = copynode(n->nhere.next);
7348 break;
7349 case NNOT:
7350 new->nnot.com = copynode(n->nnot.com);
7351 break;
7352 };
7353 new->type = n->type;
7354 return new;
7355}
7356
7357/*
7358 * Make a copy of a parse tree.
7359 */
7360static struct funcnode *
7361copyfunc(union node *n)
7362{
7363 struct funcnode *f;
7364 size_t blocksize;
7365
7366 funcblocksize = offsetof(struct funcnode, n);
7367 funcstringsize = 0;
7368 calcsize(n);
7369 blocksize = funcblocksize;
7370 f = ckmalloc(blocksize + funcstringsize);
7371 funcblock = (char *) f + offsetof(struct funcnode, n);
7372 funcstring = (char *) f + blocksize;
7373 copynode(n);
7374 f->count = 0;
7375 return f;
7376}
7377
7378/*
7379 * Define a shell function.
7380 */
7381static void
7382defun(char *name, union node *func)
7383{
7384 struct cmdentry entry;
7385
7386 INT_OFF;
7387 entry.cmdtype = CMDFUNCTION;
7388 entry.u.func = copyfunc(func);
7389 addcmdentry(name, &entry);
7390 INT_ON;
7391}
7392
7393static int evalskip; /* set if we are skipping commands */
7394/* reasons for skipping commands (see comment on breakcmd routine) */
7395#define SKIPBREAK (1 << 0)
7396#define SKIPCONT (1 << 1)
7397#define SKIPFUNC (1 << 2)
7398#define SKIPFILE (1 << 3)
7399#define SKIPEVAL (1 << 4)
7400static int skipcount; /* number of levels to skip */
7401static int funcnest; /* depth of function calls */
7402
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007403/* forward decl way out to parsing code - dotrap needs it */
7404static int evalstring(char *s, int mask);
7405
7406/*
7407 * Called to execute a trap. Perhaps we should avoid entering new trap
7408 * handlers while we are executing a trap handler.
7409 */
7410static int
7411dotrap(void)
7412{
7413 char *p;
7414 char *q;
7415 int i;
7416 int savestatus;
7417 int skip = 0;
7418
7419 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007420 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007421 xbarrier();
7422
7423 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7424 if (!*q)
7425 continue;
7426 *q = '\0';
7427
7428 p = trap[i + 1];
7429 if (!p)
7430 continue;
7431 skip = evalstring(p, SKIPEVAL);
7432 exitstatus = savestatus;
7433 if (skip)
7434 break;
7435 }
7436
7437 return skip;
7438}
7439
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007440/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007441static void evalloop(union node *, int);
7442static void evalfor(union node *, int);
7443static void evalcase(union node *, int);
7444static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007445static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007446static void evalpipe(union node *, int);
7447static void evalcommand(union node *, int);
7448static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007449static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007450
Eric Andersen62483552001-07-10 06:09:16 +00007451/*
Eric Andersenc470f442003-07-28 09:56:35 +00007452 * Evaluate a parse tree. The value is left in the global variable
7453 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007454 */
Eric Andersenc470f442003-07-28 09:56:35 +00007455static void
7456evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007457{
Eric Andersenc470f442003-07-28 09:56:35 +00007458 int checkexit = 0;
7459 void (*evalfn)(union node *, int);
7460 unsigned isor;
7461 int status;
7462 if (n == NULL) {
7463 TRACE(("evaltree(NULL) called\n"));
7464 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007465 }
Eric Andersenc470f442003-07-28 09:56:35 +00007466 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007467 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007468 switch (n->type) {
7469 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007470#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007471 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007472 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007473 break;
7474#endif
7475 case NNOT:
7476 evaltree(n->nnot.com, EV_TESTED);
7477 status = !exitstatus;
7478 goto setstatus;
7479 case NREDIR:
7480 expredir(n->nredir.redirect);
7481 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7482 if (!status) {
7483 evaltree(n->nredir.n, flags & EV_TESTED);
7484 status = exitstatus;
7485 }
7486 popredir(0);
7487 goto setstatus;
7488 case NCMD:
7489 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007490 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007491 if (eflag && !(flags & EV_TESTED))
7492 checkexit = ~0;
7493 goto calleval;
7494 case NFOR:
7495 evalfn = evalfor;
7496 goto calleval;
7497 case NWHILE:
7498 case NUNTIL:
7499 evalfn = evalloop;
7500 goto calleval;
7501 case NSUBSHELL:
7502 case NBACKGND:
7503 evalfn = evalsubshell;
7504 goto calleval;
7505 case NPIPE:
7506 evalfn = evalpipe;
7507 goto checkexit;
7508 case NCASE:
7509 evalfn = evalcase;
7510 goto calleval;
7511 case NAND:
7512 case NOR:
7513 case NSEMI:
7514#if NAND + 1 != NOR
7515#error NAND + 1 != NOR
7516#endif
7517#if NOR + 1 != NSEMI
7518#error NOR + 1 != NSEMI
7519#endif
7520 isor = n->type - NAND;
7521 evaltree(
7522 n->nbinary.ch1,
7523 (flags | ((isor >> 1) - 1)) & EV_TESTED
7524 );
7525 if (!exitstatus == isor)
7526 break;
7527 if (!evalskip) {
7528 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007529 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007530 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007531 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007532 evalfn(n, flags);
7533 break;
7534 }
7535 break;
7536 case NIF:
7537 evaltree(n->nif.test, EV_TESTED);
7538 if (evalskip)
7539 break;
7540 if (exitstatus == 0) {
7541 n = n->nif.ifpart;
7542 goto evaln;
7543 } else if (n->nif.elsepart) {
7544 n = n->nif.elsepart;
7545 goto evaln;
7546 }
7547 goto success;
7548 case NDEFUN:
7549 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007550 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007551 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007552 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007553 exitstatus = status;
7554 break;
7555 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007556 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007557 if ((checkexit & exitstatus))
7558 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007559 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007560 goto exexit;
7561
7562 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007563 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007564 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007565 }
Eric Andersen62483552001-07-10 06:09:16 +00007566}
7567
Eric Andersenc470f442003-07-28 09:56:35 +00007568#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7569static
7570#endif
7571void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7572
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007573static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007574
7575static void
7576evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007577{
7578 int status;
7579
7580 loopnest++;
7581 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007582 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007583 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007584 int i;
7585
Eric Andersencb57d552001-06-28 07:25:16 +00007586 evaltree(n->nbinary.ch1, EV_TESTED);
7587 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007588 skipping:
7589 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007590 evalskip = 0;
7591 continue;
7592 }
7593 if (evalskip == SKIPBREAK && --skipcount <= 0)
7594 evalskip = 0;
7595 break;
7596 }
Eric Andersenc470f442003-07-28 09:56:35 +00007597 i = exitstatus;
7598 if (n->type != NWHILE)
7599 i = !i;
7600 if (i != 0)
7601 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007602 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007603 status = exitstatus;
7604 if (evalskip)
7605 goto skipping;
7606 }
7607 loopnest--;
7608 exitstatus = status;
7609}
7610
Eric Andersenc470f442003-07-28 09:56:35 +00007611static void
7612evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007613{
7614 struct arglist arglist;
7615 union node *argp;
7616 struct strlist *sp;
7617 struct stackmark smark;
7618
7619 setstackmark(&smark);
7620 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007621 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007622 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007623 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007624 if (evalskip)
7625 goto out;
7626 }
7627 *arglist.lastp = NULL;
7628
7629 exitstatus = 0;
7630 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007631 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007632 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007633 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007634 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007635 if (evalskip) {
7636 if (evalskip == SKIPCONT && --skipcount <= 0) {
7637 evalskip = 0;
7638 continue;
7639 }
7640 if (evalskip == SKIPBREAK && --skipcount <= 0)
7641 evalskip = 0;
7642 break;
7643 }
7644 }
7645 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007646 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007647 popstackmark(&smark);
7648}
7649
Eric Andersenc470f442003-07-28 09:56:35 +00007650static void
7651evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007652{
7653 union node *cp;
7654 union node *patp;
7655 struct arglist arglist;
7656 struct stackmark smark;
7657
7658 setstackmark(&smark);
7659 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007660 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007661 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007662 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7663 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007664 if (casematch(patp, arglist.list->text)) {
7665 if (evalskip == 0) {
7666 evaltree(cp->nclist.body, flags);
7667 }
7668 goto out;
7669 }
7670 }
7671 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007672 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007673 popstackmark(&smark);
7674}
7675
Eric Andersenc470f442003-07-28 09:56:35 +00007676/*
7677 * Kick off a subshell to evaluate a tree.
7678 */
Eric Andersenc470f442003-07-28 09:56:35 +00007679static void
7680evalsubshell(union node *n, int flags)
7681{
7682 struct job *jp;
7683 int backgnd = (n->type == NBACKGND);
7684 int status;
7685
7686 expredir(n->nredir.redirect);
7687 if (!backgnd && flags & EV_EXIT && !trap[0])
7688 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007689 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007690 jp = makejob(n, 1);
7691 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007692 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007693 flags |= EV_EXIT;
7694 if (backgnd)
7695 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007696 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007697 redirect(n->nredir.redirect, 0);
7698 evaltreenr(n->nredir.n, flags);
7699 /* never returns */
7700 }
7701 status = 0;
7702 if (! backgnd)
7703 status = waitforjob(jp);
7704 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007705 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007706}
7707
Eric Andersenc470f442003-07-28 09:56:35 +00007708/*
7709 * Compute the names of the files in a redirection list.
7710 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007711static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007712static void
7713expredir(union node *n)
7714{
7715 union node *redir;
7716
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007717 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007718 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007719
7720 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007721 fn.lastp = &fn.list;
7722 switch (redir->type) {
7723 case NFROMTO:
7724 case NFROM:
7725 case NTO:
7726 case NCLOBBER:
7727 case NAPPEND:
7728 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7729 redir->nfile.expfname = fn.list->text;
7730 break;
7731 case NFROMFD:
7732 case NTOFD:
7733 if (redir->ndup.vname) {
7734 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007735 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007736 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007737 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007738 }
7739 break;
7740 }
7741 }
7742}
7743
Eric Andersencb57d552001-06-28 07:25:16 +00007744/*
Eric Andersencb57d552001-06-28 07:25:16 +00007745 * Evaluate a pipeline. All the processes in the pipeline are children
7746 * of the process creating the pipeline. (This differs from some versions
7747 * of the shell, which make the last process in a pipeline the parent
7748 * of all the rest.)
7749 */
Eric Andersenc470f442003-07-28 09:56:35 +00007750static void
7751evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007752{
7753 struct job *jp;
7754 struct nodelist *lp;
7755 int pipelen;
7756 int prevfd;
7757 int pip[2];
7758
Eric Andersenc470f442003-07-28 09:56:35 +00007759 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007760 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007761 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007762 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007763 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007764 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007765 jp = makejob(n, pipelen);
7766 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007767 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007768 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007769 pip[1] = -1;
7770 if (lp->next) {
7771 if (pipe(pip) < 0) {
7772 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007773 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007774 }
7775 }
7776 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007777 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007778 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007779 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007780 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007781 if (prevfd > 0) {
7782 dup2(prevfd, 0);
7783 close(prevfd);
7784 }
7785 if (pip[1] > 1) {
7786 dup2(pip[1], 1);
7787 close(pip[1]);
7788 }
Eric Andersenc470f442003-07-28 09:56:35 +00007789 evaltreenr(lp->n, flags);
7790 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007791 }
7792 if (prevfd >= 0)
7793 close(prevfd);
7794 prevfd = pip[0];
7795 close(pip[1]);
7796 }
Eric Andersencb57d552001-06-28 07:25:16 +00007797 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007798 exitstatus = waitforjob(jp);
7799 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007800 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007801 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007802}
7803
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007804/*
7805 * Controls whether the shell is interactive or not.
7806 */
7807static void
7808setinteractive(int on)
7809{
7810 static int is_interactive;
7811
7812 if (++on == is_interactive)
7813 return;
7814 is_interactive = on;
7815 setsignal(SIGINT);
7816 setsignal(SIGQUIT);
7817 setsignal(SIGTERM);
7818#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7819 if (is_interactive > 1) {
7820 /* Looks like they want an interactive shell */
7821 static smallint do_banner;
7822
7823 if (!do_banner) {
7824 out1fmt(
7825 "\n\n"
7826 "%s Built-in shell (ash)\n"
7827 "Enter 'help' for a list of built-in commands."
7828 "\n\n",
7829 BB_BANNER);
7830 do_banner = 1;
7831 }
7832 }
7833#endif
7834}
7835
7836#if ENABLE_FEATURE_EDITING_VI
7837#define setvimode(on) do { \
7838 if (on) line_input_state->flags |= VI_MODE; \
7839 else line_input_state->flags &= ~VI_MODE; \
7840} while (0)
7841#else
7842#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7843#endif
7844
7845static void
7846optschanged(void)
7847{
7848#if DEBUG
7849 opentrace();
7850#endif
7851 setinteractive(iflag);
7852 setjobctl(mflag);
7853 setvimode(viflag);
7854}
7855
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007856static struct localvar *localvars;
7857
7858/*
7859 * Called after a function returns.
7860 * Interrupts must be off.
7861 */
7862static void
7863poplocalvars(void)
7864{
7865 struct localvar *lvp;
7866 struct var *vp;
7867
7868 while ((lvp = localvars) != NULL) {
7869 localvars = lvp->next;
7870 vp = lvp->vp;
7871 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7872 if (vp == NULL) { /* $- saved */
7873 memcpy(optlist, lvp->text, sizeof(optlist));
7874 free((char*)lvp->text);
7875 optschanged();
7876 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7877 unsetvar(vp->text);
7878 } else {
7879 if (vp->func)
7880 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7881 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7882 free((char*)vp->text);
7883 vp->flags = lvp->flags;
7884 vp->text = lvp->text;
7885 }
7886 free(lvp);
7887 }
7888}
7889
7890static int
7891evalfun(struct funcnode *func, int argc, char **argv, int flags)
7892{
7893 volatile struct shparam saveparam;
7894 struct localvar *volatile savelocalvars;
7895 struct jmploc *volatile savehandler;
7896 struct jmploc jmploc;
7897 int e;
7898
7899 saveparam = shellparam;
7900 savelocalvars = localvars;
7901 e = setjmp(jmploc.loc);
7902 if (e) {
7903 goto funcdone;
7904 }
7905 INT_OFF;
7906 savehandler = exception_handler;
7907 exception_handler = &jmploc;
7908 localvars = NULL;
7909 shellparam.malloc = 0;
7910 func->count++;
7911 funcnest++;
7912 INT_ON;
7913 shellparam.nparam = argc - 1;
7914 shellparam.p = argv + 1;
7915#if ENABLE_ASH_GETOPTS
7916 shellparam.optind = 1;
7917 shellparam.optoff = -1;
7918#endif
7919 evaltree(&func->n, flags & EV_TESTED);
7920funcdone:
7921 INT_OFF;
7922 funcnest--;
7923 freefunc(func);
7924 poplocalvars();
7925 localvars = savelocalvars;
7926 freeparam(&shellparam);
7927 shellparam = saveparam;
7928 exception_handler = savehandler;
7929 INT_ON;
7930 evalskip &= ~SKIPFUNC;
7931 return e;
7932}
7933
Denis Vlasenko131ae172007-02-18 13:00:19 +00007934#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007935static char **
7936parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007937{
7938 char *cp, c;
7939
7940 for (;;) {
7941 cp = *++argv;
7942 if (!cp)
7943 return 0;
7944 if (*cp++ != '-')
7945 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007946 c = *cp++;
7947 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007948 break;
7949 if (c == '-' && !*cp) {
7950 argv++;
7951 break;
7952 }
7953 do {
7954 switch (c) {
7955 case 'p':
7956 *path = defpath;
7957 break;
7958 default:
7959 /* run 'typecmd' for other options */
7960 return 0;
7961 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007962 c = *cp++;
7963 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007964 }
7965 return argv;
7966}
7967#endif
7968
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007969/*
7970 * Make a variable a local variable. When a variable is made local, it's
7971 * value and flags are saved in a localvar structure. The saved values
7972 * will be restored when the shell function returns. We handle the name
7973 * "-" as a special case.
7974 */
7975static void
7976mklocal(char *name)
7977{
7978 struct localvar *lvp;
7979 struct var **vpp;
7980 struct var *vp;
7981
7982 INT_OFF;
7983 lvp = ckmalloc(sizeof(struct localvar));
7984 if (LONE_DASH(name)) {
7985 char *p;
7986 p = ckmalloc(sizeof(optlist));
7987 lvp->text = memcpy(p, optlist, sizeof(optlist));
7988 vp = NULL;
7989 } else {
7990 char *eq;
7991
7992 vpp = hashvar(name);
7993 vp = *findvar(vpp, name);
7994 eq = strchr(name, '=');
7995 if (vp == NULL) {
7996 if (eq)
7997 setvareq(name, VSTRFIXED);
7998 else
7999 setvar(name, NULL, VSTRFIXED);
8000 vp = *vpp; /* the new variable */
8001 lvp->flags = VUNSET;
8002 } else {
8003 lvp->text = vp->text;
8004 lvp->flags = vp->flags;
8005 vp->flags |= VSTRFIXED|VTEXTFIXED;
8006 if (eq)
8007 setvareq(name, 0);
8008 }
8009 }
8010 lvp->vp = vp;
8011 lvp->next = localvars;
8012 localvars = lvp;
8013 INT_ON;
8014}
8015
8016/*
8017 * The "local" command.
8018 */
8019static int
8020localcmd(int argc, char **argv)
8021{
8022 char *name;
8023
8024 argv = argptr;
8025 while ((name = *argv++) != NULL) {
8026 mklocal(name);
8027 }
8028 return 0;
8029}
8030
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008031static int
8032falsecmd(int argc, char **argv)
8033{
8034 return 1;
8035}
8036
8037static int
8038truecmd(int argc, char **argv)
8039{
8040 return 0;
8041}
8042
8043static int
8044execcmd(int argc, char **argv)
8045{
8046 if (argc > 1) {
8047 iflag = 0; /* exit on error */
8048 mflag = 0;
8049 optschanged();
8050 shellexec(argv + 1, pathval(), 0);
8051 }
8052 return 0;
8053}
8054
8055/*
8056 * The return command.
8057 */
8058static int
8059returncmd(int argc, char **argv)
8060{
8061 /*
8062 * If called outside a function, do what ksh does;
8063 * skip the rest of the file.
8064 */
8065 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8066 return argv[1] ? number(argv[1]) : exitstatus;
8067}
8068
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008069/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008070static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008071static int dotcmd(int, char **);
8072static int evalcmd(int, char **);
8073#if ENABLE_ASH_BUILTIN_ECHO
8074static int echocmd(int, char **);
8075#endif
8076#if ENABLE_ASH_BUILTIN_TEST
8077static int testcmd(int, char **);
8078#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008079static int exitcmd(int, char **);
8080static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008081#if ENABLE_ASH_GETOPTS
8082static int getoptscmd(int, char **);
8083#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008084#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8085static int helpcmd(int argc, char **argv);
8086#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008087#if ENABLE_ASH_MATH_SUPPORT
8088static int letcmd(int, char **);
8089#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008090static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008091static int setcmd(int, char **);
8092static int shiftcmd(int, char **);
8093static int timescmd(int, char **);
8094static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008095static int umaskcmd(int, char **);
8096static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008097static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008098
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008099#define BUILTIN_NOSPEC "0"
8100#define BUILTIN_SPECIAL "1"
8101#define BUILTIN_REGULAR "2"
8102#define BUILTIN_SPEC_REG "3"
8103#define BUILTIN_ASSIGN "4"
8104#define BUILTIN_SPEC_ASSG "5"
8105#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008106#define BUILTIN_SPEC_REG_ASSG "7"
8107
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008108/* make sure to keep these in proper order since it is searched via bsearch() */
8109static const struct builtincmd builtintab[] = {
8110 { BUILTIN_SPEC_REG ".", dotcmd },
8111 { BUILTIN_SPEC_REG ":", truecmd },
8112#if ENABLE_ASH_BUILTIN_TEST
8113 { BUILTIN_REGULAR "[", testcmd },
8114 { BUILTIN_REGULAR "[[", testcmd },
8115#endif
8116#if ENABLE_ASH_ALIAS
8117 { BUILTIN_REG_ASSG "alias", aliascmd },
8118#endif
8119#if JOBS
8120 { BUILTIN_REGULAR "bg", fg_bgcmd },
8121#endif
8122 { BUILTIN_SPEC_REG "break", breakcmd },
8123 { BUILTIN_REGULAR "cd", cdcmd },
8124 { BUILTIN_NOSPEC "chdir", cdcmd },
8125#if ENABLE_ASH_CMDCMD
8126 { BUILTIN_REGULAR "command", commandcmd },
8127#endif
8128 { BUILTIN_SPEC_REG "continue", breakcmd },
8129#if ENABLE_ASH_BUILTIN_ECHO
8130 { BUILTIN_REGULAR "echo", echocmd },
8131#endif
8132 { BUILTIN_SPEC_REG "eval", evalcmd },
8133 { BUILTIN_SPEC_REG "exec", execcmd },
8134 { BUILTIN_SPEC_REG "exit", exitcmd },
8135 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8136 { BUILTIN_REGULAR "false", falsecmd },
8137#if JOBS
8138 { BUILTIN_REGULAR "fg", fg_bgcmd },
8139#endif
8140#if ENABLE_ASH_GETOPTS
8141 { BUILTIN_REGULAR "getopts", getoptscmd },
8142#endif
8143 { BUILTIN_NOSPEC "hash", hashcmd },
8144#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8145 { BUILTIN_NOSPEC "help", helpcmd },
8146#endif
8147#if JOBS
8148 { BUILTIN_REGULAR "jobs", jobscmd },
8149 { BUILTIN_REGULAR "kill", killcmd },
8150#endif
8151#if ENABLE_ASH_MATH_SUPPORT
8152 { BUILTIN_NOSPEC "let", letcmd },
8153#endif
8154 { BUILTIN_ASSIGN "local", localcmd },
8155 { BUILTIN_NOSPEC "pwd", pwdcmd },
8156 { BUILTIN_REGULAR "read", readcmd },
8157 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8158 { BUILTIN_SPEC_REG "return", returncmd },
8159 { BUILTIN_SPEC_REG "set", setcmd },
8160 { BUILTIN_SPEC_REG "shift", shiftcmd },
8161 { BUILTIN_SPEC_REG "source", dotcmd },
8162#if ENABLE_ASH_BUILTIN_TEST
8163 { BUILTIN_REGULAR "test", testcmd },
8164#endif
8165 { BUILTIN_SPEC_REG "times", timescmd },
8166 { BUILTIN_SPEC_REG "trap", trapcmd },
8167 { BUILTIN_REGULAR "true", truecmd },
8168 { BUILTIN_NOSPEC "type", typecmd },
8169 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8170 { BUILTIN_REGULAR "umask", umaskcmd },
8171#if ENABLE_ASH_ALIAS
8172 { BUILTIN_REGULAR "unalias", unaliascmd },
8173#endif
8174 { BUILTIN_SPEC_REG "unset", unsetcmd },
8175 { BUILTIN_REGULAR "wait", waitcmd },
8176};
8177
8178#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
8179
8180#define COMMANDCMD (builtintab + 5 + \
8181 2 * ENABLE_ASH_BUILTIN_TEST + \
8182 ENABLE_ASH_ALIAS + \
8183 ENABLE_ASH_JOB_CONTROL)
8184#define EXECCMD (builtintab + 7 + \
8185 2 * ENABLE_ASH_BUILTIN_TEST + \
8186 ENABLE_ASH_ALIAS + \
8187 ENABLE_ASH_JOB_CONTROL + \
8188 ENABLE_ASH_CMDCMD + \
8189 ENABLE_ASH_BUILTIN_ECHO)
8190
8191/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008192 * Search the table of builtin commands.
8193 */
8194static struct builtincmd *
8195find_builtin(const char *name)
8196{
8197 struct builtincmd *bp;
8198
8199 bp = bsearch(
8200 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
8201 pstrcmp
8202 );
8203 return bp;
8204}
8205
8206/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008207 * Execute a simple command.
8208 */
8209static int back_exitstatus; /* exit status of backquoted command */
8210static int
8211isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008212{
8213 const char *q = endofname(p);
8214 if (p == q)
8215 return 0;
8216 return *q == '=';
8217}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008218static int
8219bltincmd(int argc, char **argv)
8220{
8221 /* Preserve exitstatus of a previous possible redirection
8222 * as POSIX mandates */
8223 return back_exitstatus;
8224}
Eric Andersenc470f442003-07-28 09:56:35 +00008225static void
8226evalcommand(union node *cmd, int flags)
8227{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008228 static const struct builtincmd bltin = {
8229 "\0\0", bltincmd
8230 };
Eric Andersenc470f442003-07-28 09:56:35 +00008231 struct stackmark smark;
8232 union node *argp;
8233 struct arglist arglist;
8234 struct arglist varlist;
8235 char **argv;
8236 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008237 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008238 struct cmdentry cmdentry;
8239 struct job *jp;
8240 char *lastarg;
8241 const char *path;
8242 int spclbltin;
8243 int cmd_is_exec;
8244 int status;
8245 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008246 struct builtincmd *bcmd;
8247 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008248
8249 /* First expand the arguments. */
8250 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8251 setstackmark(&smark);
8252 back_exitstatus = 0;
8253
8254 cmdentry.cmdtype = CMDBUILTIN;
8255 cmdentry.u.cmd = &bltin;
8256 varlist.lastp = &varlist.list;
8257 *varlist.lastp = NULL;
8258 arglist.lastp = &arglist.list;
8259 *arglist.lastp = NULL;
8260
8261 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008262 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008263 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8264 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8265 }
8266
Eric Andersenc470f442003-07-28 09:56:35 +00008267 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8268 struct strlist **spp;
8269
8270 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008271 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008272 expandarg(argp, &arglist, EXP_VARTILDE);
8273 else
8274 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8275
Eric Andersenc470f442003-07-28 09:56:35 +00008276 for (sp = *spp; sp; sp = sp->next)
8277 argc++;
8278 }
8279
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008280 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008281 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008282 TRACE(("evalcommand arg: %s\n", sp->text));
8283 *nargv++ = sp->text;
8284 }
8285 *nargv = NULL;
8286
8287 lastarg = NULL;
8288 if (iflag && funcnest == 0 && argc > 0)
8289 lastarg = nargv[-1];
8290
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008291 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008292 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008293 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008294
8295 path = vpath.text;
8296 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8297 struct strlist **spp;
8298 char *p;
8299
8300 spp = varlist.lastp;
8301 expandarg(argp, &varlist, EXP_VARTILDE);
8302
8303 /*
8304 * Modify the command lookup path, if a PATH= assignment
8305 * is present
8306 */
8307 p = (*spp)->text;
8308 if (varequal(p, path))
8309 path = p;
8310 }
8311
8312 /* Print the command if xflag is set. */
8313 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008314 int n;
8315 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008316
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008317 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008318 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008319
8320 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008321 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008322 while (sp) {
8323 dprintf(preverrout_fd, p, sp->text);
8324 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008325 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008326 p--;
8327 }
8328 }
8329 sp = arglist.list;
8330 }
Rob Landley53437472006-07-16 08:14:35 +00008331 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008332 }
8333
8334 cmd_is_exec = 0;
8335 spclbltin = -1;
8336
8337 /* Now locate the command. */
8338 if (argc) {
8339 const char *oldpath;
8340 int cmd_flag = DO_ERR;
8341
8342 path += 5;
8343 oldpath = path;
8344 for (;;) {
8345 find_command(argv[0], &cmdentry, cmd_flag, path);
8346 if (cmdentry.cmdtype == CMDUNKNOWN) {
8347 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008348 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008349 goto bail;
8350 }
8351
8352 /* implement bltin and command here */
8353 if (cmdentry.cmdtype != CMDBUILTIN)
8354 break;
8355 if (spclbltin < 0)
8356 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8357 if (cmdentry.u.cmd == EXECCMD)
8358 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008359#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008360 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008361 path = oldpath;
8362 nargv = parse_command_args(argv, &path);
8363 if (!nargv)
8364 break;
8365 argc -= nargv - argv;
8366 argv = nargv;
8367 cmd_flag |= DO_NOFUNC;
8368 } else
8369#endif
8370 break;
8371 }
8372 }
8373
8374 if (status) {
8375 /* We have a redirection error. */
8376 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008377 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008378 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008379 exitstatus = status;
8380 goto out;
8381 }
8382
8383 /* Execute the command. */
8384 switch (cmdentry.cmdtype) {
8385 default:
8386 /* Fork off a child process if necessary. */
8387 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008388 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008389 jp = makejob(cmd, 1);
8390 if (forkshell(jp, cmd, FORK_FG) != 0) {
8391 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008392 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008393 break;
8394 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008395 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008396 }
8397 listsetvar(varlist.list, VEXPORT|VSTACK);
8398 shellexec(argv, path, cmdentry.u.index);
8399 /* NOTREACHED */
8400
8401 case CMDBUILTIN:
8402 cmdenviron = varlist.list;
8403 if (cmdenviron) {
8404 struct strlist *list = cmdenviron;
8405 int i = VNOSET;
8406 if (spclbltin > 0 || argc == 0) {
8407 i = 0;
8408 if (cmd_is_exec && argc > 1)
8409 i = VEXPORT;
8410 }
8411 listsetvar(list, i);
8412 }
8413 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8414 int exit_status;
8415 int i, j;
8416
8417 i = exception;
8418 if (i == EXEXIT)
8419 goto raise;
8420
8421 exit_status = 2;
8422 j = 0;
8423 if (i == EXINT)
8424 j = SIGINT;
8425 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008426 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008427 if (j)
8428 exit_status = j + 128;
8429 exitstatus = exit_status;
8430
8431 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008432 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008433 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008434 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008435 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008436 }
8437 break;
8438
8439 case CMDFUNCTION:
8440 listsetvar(varlist.list, 0);
8441 if (evalfun(cmdentry.u.func, argc, argv, flags))
8442 goto raise;
8443 break;
8444 }
8445
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008446 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008447 popredir(cmd_is_exec);
8448 if (lastarg)
8449 /* dsl: I think this is intended to be used to support
8450 * '_' in 'vi' command mode during line editing...
8451 * However I implemented that within libedit itself.
8452 */
8453 setvar("_", lastarg, 0);
8454 popstackmark(&smark);
8455}
8456
8457static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008458evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8459{
Eric Andersenc470f442003-07-28 09:56:35 +00008460 char *volatile savecmdname;
8461 struct jmploc *volatile savehandler;
8462 struct jmploc jmploc;
8463 int i;
8464
8465 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008466 i = setjmp(jmploc.loc);
8467 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008468 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008469 savehandler = exception_handler;
8470 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008471 commandname = argv[0];
8472 argptr = argv + 1;
8473 optptr = NULL; /* initialize nextopt */
8474 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008475 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008476 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008477 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008478 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008479 commandname = savecmdname;
8480 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008481 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008482
8483 return i;
8484}
8485
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008486static int
8487goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008488{
8489 return !*endofname(p);
8490}
8491
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008492
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008493/*
8494 * Search for a command. This is called before we fork so that the
8495 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008496 * the child. The check for "goodname" is an overly conservative
8497 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008498 */
Eric Andersenc470f442003-07-28 09:56:35 +00008499static void
8500prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008501{
8502 struct cmdentry entry;
8503
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008504 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8505 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008506}
8507
Eric Andersencb57d552001-06-28 07:25:16 +00008508
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008509/* ============ Builtin commands
8510 *
8511 * Builtin commands whose functions are closely tied to evaluation
8512 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008513 */
8514
8515/*
Eric Andersencb57d552001-06-28 07:25:16 +00008516 * Handle break and continue commands. Break, continue, and return are
8517 * all handled by setting the evalskip flag. The evaluation routines
8518 * above all check this flag, and if it is set they start skipping
8519 * commands rather than executing them. The variable skipcount is
8520 * the number of loops to break/continue, or the number of function
8521 * levels to return. (The latter is always 1.) It should probably
8522 * be an error to break out of more loops than exist, but it isn't
8523 * in the standard shell so we don't make it one here.
8524 */
Eric Andersenc470f442003-07-28 09:56:35 +00008525static int
8526breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008527{
8528 int n = argc > 1 ? number(argv[1]) : 1;
8529
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008530 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008531 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008532 if (n > loopnest)
8533 n = loopnest;
8534 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008535 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008536 skipcount = n;
8537 }
8538 return 0;
8539}
8540
Eric Andersenc470f442003-07-28 09:56:35 +00008541
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008542/* ============ input.c
8543 *
Eric Andersen90898442003-08-06 11:20:52 +00008544 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008545 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008546
Eric Andersenc470f442003-07-28 09:56:35 +00008547#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008548
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008549enum {
8550 INPUT_PUSH_FILE = 1,
8551 INPUT_NOFILE_OK = 2,
8552};
Eric Andersencb57d552001-06-28 07:25:16 +00008553
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008554/*
8555 * NEOF is returned by parsecmd when it encounters an end of file. It
8556 * must be distinct from NULL, so we use the address of a variable that
8557 * happens to be handy.
8558 */
8559static int plinno = 1; /* input line number */
8560/* number of characters left in input buffer */
8561static int parsenleft; /* copy of parsefile->nleft */
8562static int parselleft; /* copy of parsefile->lleft */
8563/* next character in input buffer */
8564static char *parsenextc; /* copy of parsefile->nextc */
8565
8566static int checkkwd;
8567/* values of checkkwd variable */
8568#define CHKALIAS 0x1
8569#define CHKKWD 0x2
8570#define CHKNL 0x4
8571
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008572static void
8573popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008574{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008575 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008576
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008577 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008578#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008579 if (sp->ap) {
8580 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8581 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008582 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008583 if (sp->string != sp->ap->val) {
8584 free(sp->string);
8585 }
8586 sp->ap->flag &= ~ALIASINUSE;
8587 if (sp->ap->flag & ALIASDEAD) {
8588 unalias(sp->ap->name);
8589 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008590 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008591#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008592 parsenextc = sp->prevstring;
8593 parsenleft = sp->prevnleft;
8594/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8595 parsefile->strpush = sp->prev;
8596 if (sp != &(parsefile->basestrpush))
8597 free(sp);
8598 INT_ON;
8599}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008600
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008601static int
8602preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008603{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008604 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008605 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008606 parsenextc = buf;
8607
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008608 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008609#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008610 if (!iflag || parsefile->fd)
8611 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8612 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008613#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008614 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008615#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008616 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8617 if (nr == 0) {
8618 /* Ctrl+C pressed */
8619 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008620 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008621 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008622 raise(SIGINT);
8623 return 1;
8624 }
Eric Andersenc470f442003-07-28 09:56:35 +00008625 goto retry;
8626 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008627 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008628 /* Ctrl+D presend */
8629 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008630 }
Eric Andersencb57d552001-06-28 07:25:16 +00008631 }
8632#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008633 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008634#endif
8635
8636 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008637 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
8638 int flags = fcntl(0, F_GETFL, 0);
8639 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008640 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008641 if (fcntl(0, F_SETFL, flags) >= 0) {
8642 out2str("sh: turning off NDELAY mode\n");
8643 goto retry;
8644 }
8645 }
8646 }
8647 }
8648 return nr;
8649}
8650
8651/*
8652 * Refill the input buffer and return the next input character:
8653 *
8654 * 1) If a string was pushed back on the input, pop it;
8655 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8656 * from a string so we can't refill the buffer, return EOF.
8657 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8658 * 4) Process input up to the next newline, deleting nul characters.
8659 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008660static int
Eric Andersenc470f442003-07-28 09:56:35 +00008661preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008662{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008663 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008664 int more;
8665 char savec;
8666
8667 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008668#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008669 if (parsenleft == -1 && parsefile->strpush->ap &&
8670 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008671 return PEOA;
8672 }
Eric Andersen2870d962001-07-02 17:27:21 +00008673#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008674 popstring();
8675 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008676 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008677 }
8678 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8679 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008680 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008681
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008682 more = parselleft;
8683 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008684 again:
8685 more = preadfd();
8686 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008687 parselleft = parsenleft = EOF_NLEFT;
8688 return PEOF;
8689 }
8690 }
8691
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008692 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008693
8694 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008695 for (;;) {
8696 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008697
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008698 more--;
8699 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008700
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008701 if (!c)
8702 memmove(q, q + 1, more);
8703 else {
8704 q++;
8705 if (c == '\n') {
8706 parsenleft = q - parsenextc - 1;
8707 break;
8708 }
Eric Andersencb57d552001-06-28 07:25:16 +00008709 }
8710
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008711 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008712 parsenleft = q - parsenextc - 1;
8713 if (parsenleft < 0)
8714 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008715 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008716 }
8717 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008718 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008719
8720 savec = *q;
8721 *q = '\0';
8722
8723 if (vflag) {
8724 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008725 }
8726
8727 *q = savec;
8728
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008729 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008730}
8731
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008732#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008733static int
8734pgetc(void)
8735{
8736 return pgetc_as_macro();
8737}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008738
8739#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8740#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008741#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008742#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008743#endif
8744
8745/*
8746 * Same as pgetc(), but ignores PEOA.
8747 */
8748#if ENABLE_ASH_ALIAS
8749static int
8750pgetc2(void)
8751{
8752 int c;
8753
8754 do {
8755 c = pgetc_macro();
8756 } while (c == PEOA);
8757 return c;
8758}
8759#else
8760static int
8761pgetc2(void)
8762{
8763 return pgetc_macro();
8764}
8765#endif
8766
8767/*
8768 * Read a line from the script.
8769 */
8770static char *
8771pfgets(char *line, int len)
8772{
8773 char *p = line;
8774 int nleft = len;
8775 int c;
8776
8777 while (--nleft > 0) {
8778 c = pgetc2();
8779 if (c == PEOF) {
8780 if (p == line)
8781 return NULL;
8782 break;
8783 }
8784 *p++ = c;
8785 if (c == '\n')
8786 break;
8787 }
8788 *p = '\0';
8789 return line;
8790}
8791
Eric Andersenc470f442003-07-28 09:56:35 +00008792/*
8793 * Undo the last call to pgetc. Only one character may be pushed back.
8794 * PEOF may be pushed back.
8795 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008796static void
Eric Andersenc470f442003-07-28 09:56:35 +00008797pungetc(void)
8798{
8799 parsenleft++;
8800 parsenextc--;
8801}
Eric Andersencb57d552001-06-28 07:25:16 +00008802
8803/*
8804 * Push a string back onto the input at this current parsefile level.
8805 * We handle aliases this way.
8806 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008807static void
Eric Andersenc470f442003-07-28 09:56:35 +00008808pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008809{
Eric Andersencb57d552001-06-28 07:25:16 +00008810 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008811 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008812
Eric Andersenc470f442003-07-28 09:56:35 +00008813 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008814 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008815/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8816 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008817 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008818 sp->prev = parsefile->strpush;
8819 parsefile->strpush = sp;
8820 } else
8821 sp = parsefile->strpush = &(parsefile->basestrpush);
8822 sp->prevstring = parsenextc;
8823 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008824#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008825 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008826 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008827 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008828 sp->string = s;
8829 }
Eric Andersen2870d962001-07-02 17:27:21 +00008830#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008831 parsenextc = s;
8832 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008833 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008834}
8835
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008836/*
8837 * To handle the "." command, a stack of input files is used. Pushfile
8838 * adds a new entry to the stack and popfile restores the previous level.
8839 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008840static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008841pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008842{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008843 struct parsefile *pf;
8844
8845 parsefile->nleft = parsenleft;
8846 parsefile->lleft = parselleft;
8847 parsefile->nextc = parsenextc;
8848 parsefile->linno = plinno;
8849 pf = ckmalloc(sizeof(*pf));
8850 pf->prev = parsefile;
8851 pf->fd = -1;
8852 pf->strpush = NULL;
8853 pf->basestrpush.prev = NULL;
8854 parsefile = pf;
8855}
8856
8857static void
8858popfile(void)
8859{
8860 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008861
Denis Vlasenkob012b102007-02-19 22:43:01 +00008862 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008863 if (pf->fd >= 0)
8864 close(pf->fd);
8865 if (pf->buf)
8866 free(pf->buf);
8867 while (pf->strpush)
8868 popstring();
8869 parsefile = pf->prev;
8870 free(pf);
8871 parsenleft = parsefile->nleft;
8872 parselleft = parsefile->lleft;
8873 parsenextc = parsefile->nextc;
8874 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008875 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008876}
8877
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008878/*
8879 * Return to top level.
8880 */
8881static void
8882popallfiles(void)
8883{
8884 while (parsefile != &basepf)
8885 popfile();
8886}
8887
8888/*
8889 * Close the file(s) that the shell is reading commands from. Called
8890 * after a fork is done.
8891 */
8892static void
8893closescript(void)
8894{
8895 popallfiles();
8896 if (parsefile->fd > 0) {
8897 close(parsefile->fd);
8898 parsefile->fd = 0;
8899 }
8900}
8901
8902/*
8903 * Like setinputfile, but takes an open file descriptor. Call this with
8904 * interrupts off.
8905 */
8906static void
8907setinputfd(int fd, int push)
8908{
8909 fcntl(fd, F_SETFD, FD_CLOEXEC);
8910 if (push) {
8911 pushfile();
8912 parsefile->buf = 0;
8913 }
8914 parsefile->fd = fd;
8915 if (parsefile->buf == NULL)
8916 parsefile->buf = ckmalloc(IBUFSIZ);
8917 parselleft = parsenleft = 0;
8918 plinno = 1;
8919}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008920
Eric Andersenc470f442003-07-28 09:56:35 +00008921/*
8922 * Set the input to take input from a file. If push is set, push the
8923 * old input onto the stack first.
8924 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008925static int
8926setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008927{
8928 int fd;
8929 int fd2;
8930
Denis Vlasenkob012b102007-02-19 22:43:01 +00008931 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008932 fd = open(fname, O_RDONLY);
8933 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008934 if (flags & INPUT_NOFILE_OK)
8935 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008936 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008937 }
Eric Andersenc470f442003-07-28 09:56:35 +00008938 if (fd < 10) {
8939 fd2 = copyfd(fd, 10);
8940 close(fd);
8941 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008942 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008943 fd = fd2;
8944 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008945 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008946 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008947 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008948 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008949}
8950
Eric Andersencb57d552001-06-28 07:25:16 +00008951/*
8952 * Like setinputfile, but takes input from a string.
8953 */
Eric Andersenc470f442003-07-28 09:56:35 +00008954static void
8955setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008956{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008957 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008958 pushfile();
8959 parsenextc = string;
8960 parsenleft = strlen(string);
8961 parsefile->buf = NULL;
8962 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008963 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008964}
8965
8966
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008967/* ============ mail.c
8968 *
8969 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008970 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008971
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008972#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008973
Eric Andersencb57d552001-06-28 07:25:16 +00008974#define MAXMBOXES 10
8975
Eric Andersenc470f442003-07-28 09:56:35 +00008976/* times of mailboxes */
8977static time_t mailtime[MAXMBOXES];
8978/* Set if MAIL or MAILPATH is changed. */
8979static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008980
Eric Andersencb57d552001-06-28 07:25:16 +00008981/*
Eric Andersenc470f442003-07-28 09:56:35 +00008982 * Print appropriate message(s) if mail has arrived.
8983 * If mail_var_path_changed is set,
8984 * then the value of MAIL has mail_var_path_changed,
8985 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008986 */
Eric Andersenc470f442003-07-28 09:56:35 +00008987static void
8988chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008989{
Eric Andersencb57d552001-06-28 07:25:16 +00008990 const char *mpath;
8991 char *p;
8992 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008993 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008994 struct stackmark smark;
8995 struct stat statb;
8996
Eric Andersencb57d552001-06-28 07:25:16 +00008997 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008998 mpath = mpathset() ? mpathval() : mailval();
8999 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009000 p = padvance(&mpath, nullstr);
9001 if (p == NULL)
9002 break;
9003 if (*p == '\0')
9004 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009005 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009006#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009007 if (q[-1] != '/')
9008 abort();
9009#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009010 q[-1] = '\0'; /* delete trailing '/' */
9011 if (stat(p, &statb) < 0) {
9012 *mtp = 0;
9013 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009014 }
Eric Andersenc470f442003-07-28 09:56:35 +00009015 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9016 fprintf(
9017 stderr, snlfmt,
9018 pathopt ? pathopt : "you have mail"
9019 );
9020 }
9021 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009022 }
Eric Andersenc470f442003-07-28 09:56:35 +00009023 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009024 popstackmark(&smark);
9025}
Eric Andersencb57d552001-06-28 07:25:16 +00009026
Eric Andersenc470f442003-07-28 09:56:35 +00009027static void
9028changemail(const char *val)
9029{
9030 mail_var_path_changed++;
9031}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009032
Denis Vlasenko131ae172007-02-18 13:00:19 +00009033#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009034
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009035
9036/* ============ ??? */
9037
Eric Andersencb57d552001-06-28 07:25:16 +00009038/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009039 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009040 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009041static void
9042setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009043{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009044 char **newparam;
9045 char **ap;
9046 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009047
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009048 for (nparam = 0; argv[nparam]; nparam++);
9049 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9050 while (*argv) {
9051 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009052 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009053 *ap = NULL;
9054 freeparam(&shellparam);
9055 shellparam.malloc = 1;
9056 shellparam.nparam = nparam;
9057 shellparam.p = newparam;
9058#if ENABLE_ASH_GETOPTS
9059 shellparam.optind = 1;
9060 shellparam.optoff = -1;
9061#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009062}
9063
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009064/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009065 * Process shell options. The global variable argptr contains a pointer
9066 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009067 */
9068static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009069minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009070{
9071 int i;
9072
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009073 if (name) {
9074 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009075 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009076 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009077 return;
9078 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009079 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009080 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009081 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009082 out1str("Current option settings\n");
9083 for (i = 0; i < NOPTS; i++)
9084 out1fmt("%-16s%s\n", optnames(i),
9085 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009086}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009087static void
9088setoption(int flag, int val)
9089{
9090 int i;
9091
9092 for (i = 0; i < NOPTS; i++) {
9093 if (optletters(i) == flag) {
9094 optlist[i] = val;
9095 return;
9096 }
9097 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009098 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009099 /* NOTREACHED */
9100}
Eric Andersenc470f442003-07-28 09:56:35 +00009101static void
9102options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009103{
9104 char *p;
9105 int val;
9106 int c;
9107
9108 if (cmdline)
9109 minusc = NULL;
9110 while ((p = *argptr) != NULL) {
9111 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009112 c = *p++;
9113 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009114 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009115 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009116 if (!cmdline) {
9117 /* "-" means turn off -x and -v */
9118 if (p[0] == '\0')
9119 xflag = vflag = 0;
9120 /* "--" means reset params */
9121 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009122 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009123 }
Eric Andersenc470f442003-07-28 09:56:35 +00009124 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009125 }
9126 } else if (c == '+') {
9127 val = 0;
9128 } else {
9129 argptr--;
9130 break;
9131 }
9132 while ((c = *p++) != '\0') {
9133 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00009134 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00009135 } else if (c == 'o') {
9136 minus_o(*argptr, val);
9137 if (*argptr)
9138 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00009139 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009140 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009141 isloginsh = 1;
9142 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009143 } else {
9144 setoption(c, val);
9145 }
9146 }
9147 }
9148}
9149
Eric Andersencb57d552001-06-28 07:25:16 +00009150/*
Eric Andersencb57d552001-06-28 07:25:16 +00009151 * The shift builtin command.
9152 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009153static int
Eric Andersenc470f442003-07-28 09:56:35 +00009154shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009155{
9156 int n;
9157 char **ap1, **ap2;
9158
9159 n = 1;
9160 if (argc > 1)
9161 n = number(argv[1]);
9162 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009163 ash_msg_and_raise_error("can't shift that many");
9164 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009165 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009166 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009167 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009168 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009169 }
9170 ap2 = shellparam.p;
9171 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009172#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009173 shellparam.optind = 1;
9174 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009175#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009176 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009177 return 0;
9178}
9179
Eric Andersencb57d552001-06-28 07:25:16 +00009180/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009181 * POSIX requires that 'set' (but not export or readonly) output the
9182 * variables in lexicographic order - by the locale's collating order (sigh).
9183 * Maybe we could keep them in an ordered balanced binary tree
9184 * instead of hashed lists.
9185 * For now just roll 'em through qsort for printing...
9186 */
9187static int
9188showvars(const char *sep_prefix, int on, int off)
9189{
9190 const char *sep;
9191 char **ep, **epend;
9192
9193 ep = listvars(on, off, &epend);
9194 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9195
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009196 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009197
9198 for (; ep < epend; ep++) {
9199 const char *p;
9200 const char *q;
9201
9202 p = strchrnul(*ep, '=');
9203 q = nullstr;
9204 if (*p)
9205 q = single_quote(++p);
9206 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9207 }
9208 return 0;
9209}
9210
9211/*
Eric Andersencb57d552001-06-28 07:25:16 +00009212 * The set command builtin.
9213 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009214static int
Eric Andersenc470f442003-07-28 09:56:35 +00009215setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009216{
9217 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009218 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009219 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009220 options(0);
9221 optschanged();
9222 if (*argptr != NULL) {
9223 setparam(argptr);
9224 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009225 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009226 return 0;
9227}
9228
Denis Vlasenko131ae172007-02-18 13:00:19 +00009229#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009230/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009231static void
9232change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009233{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009234 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009235 /* "get", generate */
9236 char buf[16];
9237
9238 rseed = rseed * 1103515245 + 12345;
9239 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9240 /* set without recursion */
9241 setvar(vrandom.text, buf, VNOFUNC);
9242 vrandom.flags &= ~VNOFUNC;
9243 } else {
9244 /* set/reset */
9245 rseed = strtoul(value, (char **)NULL, 10);
9246 }
Eric Andersenef02f822004-03-11 13:34:24 +00009247}
Eric Andersen16767e22004-03-16 05:14:10 +00009248#endif
9249
Denis Vlasenko131ae172007-02-18 13:00:19 +00009250#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009251static int
Eric Andersenc470f442003-07-28 09:56:35 +00009252getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009253{
9254 char *p, *q;
9255 char c = '?';
9256 int done = 0;
9257 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009258 char s[12];
9259 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009260
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009261 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009262 return 1;
9263 optnext = optfirst + *param_optind - 1;
9264
9265 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009266 p = NULL;
9267 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009268 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009269 if (p == NULL || *p == '\0') {
9270 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009271 p = *optnext;
9272 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009273 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009274 p = NULL;
9275 done = 1;
9276 goto out;
9277 }
9278 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009279 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009280 goto atend;
9281 }
9282
9283 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009284 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009285 if (*q == '\0') {
9286 if (optstr[0] == ':') {
9287 s[0] = c;
9288 s[1] = '\0';
9289 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009290 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009291 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009292 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009293 }
9294 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009295 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009296 }
9297 if (*++q == ':')
9298 q++;
9299 }
9300
9301 if (*++q == ':') {
9302 if (*p == '\0' && (p = *optnext) == NULL) {
9303 if (optstr[0] == ':') {
9304 s[0] = c;
9305 s[1] = '\0';
9306 err |= setvarsafe("OPTARG", s, 0);
9307 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009308 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009309 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009310 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009311 c = '?';
9312 }
Eric Andersenc470f442003-07-28 09:56:35 +00009313 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009314 }
9315
9316 if (p == *optnext)
9317 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009318 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009319 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009320 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009321 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009322 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009323 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009324 *param_optind = optnext - optfirst + 1;
9325 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009326 err |= setvarsafe("OPTIND", s, VNOFUNC);
9327 s[0] = c;
9328 s[1] = '\0';
9329 err |= setvarsafe(optvar, s, 0);
9330 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009331 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009332 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009333 flush_stdout_stderr();
9334 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009335 }
9336 return done;
9337}
Eric Andersenc470f442003-07-28 09:56:35 +00009338
9339/*
9340 * The getopts builtin. Shellparam.optnext points to the next argument
9341 * to be processed. Shellparam.optptr points to the next character to
9342 * be processed in the current argument. If shellparam.optnext is NULL,
9343 * then it's the first time getopts has been called.
9344 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009345static int
Eric Andersenc470f442003-07-28 09:56:35 +00009346getoptscmd(int argc, char **argv)
9347{
9348 char **optbase;
9349
9350 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009351 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009352 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009353 optbase = shellparam.p;
9354 if (shellparam.optind > shellparam.nparam + 1) {
9355 shellparam.optind = 1;
9356 shellparam.optoff = -1;
9357 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009358 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009359 optbase = &argv[3];
9360 if (shellparam.optind > argc - 2) {
9361 shellparam.optind = 1;
9362 shellparam.optoff = -1;
9363 }
9364 }
9365
9366 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009367 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009368}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009369#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009370
Eric Andersencb57d552001-06-28 07:25:16 +00009371
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009372/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009373
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009374static int tokpushback; /* last token pushed back */
9375#define NEOF ((union node *)&tokpushback)
9376static int parsebackquote; /* nonzero if we are inside backquotes */
9377static int lasttoken; /* last token read */
9378static char *wordtext; /* text of last word returned by readtoken */
9379static struct nodelist *backquotelist;
9380static union node *redirnode;
9381static struct heredoc *heredoc;
9382static int quoteflag; /* set if (part of) last token was quoted */
9383
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009384static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9385static void
9386raise_error_syntax(const char *msg)
9387{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009388 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009389 /* NOTREACHED */
9390}
9391
9392/*
9393 * Called when an unexpected token is read during the parse. The argument
9394 * is the token that is expected, or -1 if more than one type of token can
9395 * occur at this point.
9396 */
9397static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9398static void
9399raise_error_unexpected_syntax(int token)
9400{
9401 char msg[64];
9402 int l;
9403
9404 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9405 if (token >= 0)
9406 sprintf(msg + l, " (expecting %s)", tokname(token));
9407 raise_error_syntax(msg);
9408 /* NOTREACHED */
9409}
Eric Andersencb57d552001-06-28 07:25:16 +00009410
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009411#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009412
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009413struct heredoc {
9414 struct heredoc *next; /* next here document in list */
9415 union node *here; /* redirection node */
9416 char *eofmark; /* string indicating end of input */
9417 int striptabs; /* if set, strip leading tabs */
9418};
Eric Andersencb57d552001-06-28 07:25:16 +00009419
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009420static struct heredoc *heredoclist; /* list of here documents to read */
9421
9422/* parsing is heavily cross-recursive, need these forward decls */
9423static union node *andor(void);
9424static union node *pipeline(void);
9425static union node *parse_command(void);
9426static void parseheredoc(void);
9427static char peektoken(void);
9428static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009429
Eric Andersenc470f442003-07-28 09:56:35 +00009430static union node *
9431list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009432{
9433 union node *n1, *n2, *n3;
9434 int tok;
9435
Eric Andersenc470f442003-07-28 09:56:35 +00009436 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9437 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009438 return NULL;
9439 n1 = NULL;
9440 for (;;) {
9441 n2 = andor();
9442 tok = readtoken();
9443 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009444 if (n2->type == NPIPE) {
9445 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009446 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009447 if (n2->type != NREDIR) {
9448 n3 = stalloc(sizeof(struct nredir));
9449 n3->nredir.n = n2;
9450 n3->nredir.redirect = NULL;
9451 n2 = n3;
9452 }
9453 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009454 }
9455 }
9456 if (n1 == NULL) {
9457 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009458 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009459 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009460 n3->type = NSEMI;
9461 n3->nbinary.ch1 = n1;
9462 n3->nbinary.ch2 = n2;
9463 n1 = n3;
9464 }
9465 switch (tok) {
9466 case TBACKGND:
9467 case TSEMI:
9468 tok = readtoken();
9469 /* fall through */
9470 case TNL:
9471 if (tok == TNL) {
9472 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009473 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009474 return n1;
9475 } else {
9476 tokpushback++;
9477 }
Eric Andersenc470f442003-07-28 09:56:35 +00009478 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009479 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009480 return n1;
9481 break;
9482 case TEOF:
9483 if (heredoclist)
9484 parseheredoc();
9485 else
Eric Andersenc470f442003-07-28 09:56:35 +00009486 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009487 return n1;
9488 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009489 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009490 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009491 tokpushback++;
9492 return n1;
9493 }
9494 }
9495}
9496
Eric Andersenc470f442003-07-28 09:56:35 +00009497static union node *
9498andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009499{
Eric Andersencb57d552001-06-28 07:25:16 +00009500 union node *n1, *n2, *n3;
9501 int t;
9502
Eric Andersencb57d552001-06-28 07:25:16 +00009503 n1 = pipeline();
9504 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009505 t = readtoken();
9506 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009507 t = NAND;
9508 } else if (t == TOR) {
9509 t = NOR;
9510 } else {
9511 tokpushback++;
9512 return n1;
9513 }
Eric Andersenc470f442003-07-28 09:56:35 +00009514 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009515 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009516 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009517 n3->type = t;
9518 n3->nbinary.ch1 = n1;
9519 n3->nbinary.ch2 = n2;
9520 n1 = n3;
9521 }
9522}
9523
Eric Andersenc470f442003-07-28 09:56:35 +00009524static union node *
9525pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009526{
Eric Andersencb57d552001-06-28 07:25:16 +00009527 union node *n1, *n2, *pipenode;
9528 struct nodelist *lp, *prev;
9529 int negate;
9530
9531 negate = 0;
9532 TRACE(("pipeline: entered\n"));
9533 if (readtoken() == TNOT) {
9534 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009535 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009536 } else
9537 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009538 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009539 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009540 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009541 pipenode->type = NPIPE;
9542 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009543 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009544 pipenode->npipe.cmdlist = lp;
9545 lp->n = n1;
9546 do {
9547 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009548 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009549 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009550 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009551 prev->next = lp;
9552 } while (readtoken() == TPIPE);
9553 lp->next = NULL;
9554 n1 = pipenode;
9555 }
9556 tokpushback++;
9557 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009558 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009559 n2->type = NNOT;
9560 n2->nnot.com = n1;
9561 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009562 }
9563 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009564}
9565
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009566static union node *
9567makename(void)
9568{
9569 union node *n;
9570
9571 n = stalloc(sizeof(struct narg));
9572 n->type = NARG;
9573 n->narg.next = NULL;
9574 n->narg.text = wordtext;
9575 n->narg.backquote = backquotelist;
9576 return n;
9577}
9578
9579static void
9580fixredir(union node *n, const char *text, int err)
9581{
9582 TRACE(("Fix redir %s %d\n", text, err));
9583 if (!err)
9584 n->ndup.vname = NULL;
9585
9586 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009587 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009588 else if (LONE_DASH(text))
9589 n->ndup.dupfd = -1;
9590 else {
9591 if (err)
9592 raise_error_syntax("Bad fd number");
9593 n->ndup.vname = makename();
9594 }
9595}
9596
9597/*
9598 * Returns true if the text contains nothing to expand (no dollar signs
9599 * or backquotes).
9600 */
9601static int
9602noexpand(char *text)
9603{
9604 char *p;
9605 char c;
9606
9607 p = text;
9608 while ((c = *p++) != '\0') {
9609 if (c == CTLQUOTEMARK)
9610 continue;
9611 if (c == CTLESC)
9612 p++;
9613 else if (SIT(c, BASESYNTAX) == CCTL)
9614 return 0;
9615 }
9616 return 1;
9617}
9618
9619static void
9620parsefname(void)
9621{
9622 union node *n = redirnode;
9623
9624 if (readtoken() != TWORD)
9625 raise_error_unexpected_syntax(-1);
9626 if (n->type == NHERE) {
9627 struct heredoc *here = heredoc;
9628 struct heredoc *p;
9629 int i;
9630
9631 if (quoteflag == 0)
9632 n->type = NXHERE;
9633 TRACE(("Here document %d\n", n->type));
9634 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9635 raise_error_syntax("Illegal eof marker for << redirection");
9636 rmescapes(wordtext);
9637 here->eofmark = wordtext;
9638 here->next = NULL;
9639 if (heredoclist == NULL)
9640 heredoclist = here;
9641 else {
9642 for (p = heredoclist; p->next; p = p->next);
9643 p->next = here;
9644 }
9645 } else if (n->type == NTOFD || n->type == NFROMFD) {
9646 fixredir(n, wordtext, 0);
9647 } else {
9648 n->nfile.fname = makename();
9649 }
9650}
Eric Andersencb57d552001-06-28 07:25:16 +00009651
Eric Andersenc470f442003-07-28 09:56:35 +00009652static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009653simplecmd(void)
9654{
9655 union node *args, **app;
9656 union node *n = NULL;
9657 union node *vars, **vpp;
9658 union node **rpp, *redir;
9659 int savecheckkwd;
9660
9661 args = NULL;
9662 app = &args;
9663 vars = NULL;
9664 vpp = &vars;
9665 redir = NULL;
9666 rpp = &redir;
9667
9668 savecheckkwd = CHKALIAS;
9669 for (;;) {
9670 checkkwd = savecheckkwd;
9671 switch (readtoken()) {
9672 case TWORD:
9673 n = stalloc(sizeof(struct narg));
9674 n->type = NARG;
9675 n->narg.text = wordtext;
9676 n->narg.backquote = backquotelist;
9677 if (savecheckkwd && isassignment(wordtext)) {
9678 *vpp = n;
9679 vpp = &n->narg.next;
9680 } else {
9681 *app = n;
9682 app = &n->narg.next;
9683 savecheckkwd = 0;
9684 }
9685 break;
9686 case TREDIR:
9687 *rpp = n = redirnode;
9688 rpp = &n->nfile.next;
9689 parsefname(); /* read name of redirection file */
9690 break;
9691 case TLP:
9692 if (args && app == &args->narg.next
9693 && !vars && !redir
9694 ) {
9695 struct builtincmd *bcmd;
9696 const char *name;
9697
9698 /* We have a function */
9699 if (readtoken() != TRP)
9700 raise_error_unexpected_syntax(TRP);
9701 name = n->narg.text;
9702 if (!goodname(name)
9703 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9704 ) {
9705 raise_error_syntax("Bad function name");
9706 }
9707 n->type = NDEFUN;
9708 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9709 n->narg.next = parse_command();
9710 return n;
9711 }
9712 /* fall through */
9713 default:
9714 tokpushback++;
9715 goto out;
9716 }
9717 }
9718 out:
9719 *app = NULL;
9720 *vpp = NULL;
9721 *rpp = NULL;
9722 n = stalloc(sizeof(struct ncmd));
9723 n->type = NCMD;
9724 n->ncmd.args = args;
9725 n->ncmd.assign = vars;
9726 n->ncmd.redirect = redir;
9727 return n;
9728}
9729
9730static union node *
9731parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009732{
Eric Andersencb57d552001-06-28 07:25:16 +00009733 union node *n1, *n2;
9734 union node *ap, **app;
9735 union node *cp, **cpp;
9736 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009737 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009738 int t;
9739
9740 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009741 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009742
Eric Andersencb57d552001-06-28 07:25:16 +00009743 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009744 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009745 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009746 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009747 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009748 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009749 n1->type = NIF;
9750 n1->nif.test = list(0);
9751 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009752 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009753 n1->nif.ifpart = list(0);
9754 n2 = n1;
9755 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009756 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009757 n2 = n2->nif.elsepart;
9758 n2->type = NIF;
9759 n2->nif.test = list(0);
9760 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009761 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009762 n2->nif.ifpart = list(0);
9763 }
9764 if (lasttoken == TELSE)
9765 n2->nif.elsepart = list(0);
9766 else {
9767 n2->nif.elsepart = NULL;
9768 tokpushback++;
9769 }
Eric Andersenc470f442003-07-28 09:56:35 +00009770 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009771 break;
9772 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009773 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009774 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009775 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009776 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009777 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009778 got = readtoken();
9779 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009780 TRACE(("expecting DO got %s %s\n", tokname(got),
9781 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009782 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009783 }
9784 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009785 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009786 break;
9787 }
9788 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009789 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009790 raise_error_syntax("Bad for loop variable");
9791 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009792 n1->type = NFOR;
9793 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009794 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009795 if (readtoken() == TIN) {
9796 app = &ap;
9797 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009798 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009799 n2->type = NARG;
9800 n2->narg.text = wordtext;
9801 n2->narg.backquote = backquotelist;
9802 *app = n2;
9803 app = &n2->narg.next;
9804 }
9805 *app = NULL;
9806 n1->nfor.args = ap;
9807 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009808 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009809 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009810 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009811 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009812 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009813 n2->narg.backquote = NULL;
9814 n2->narg.next = NULL;
9815 n1->nfor.args = n2;
9816 /*
9817 * Newline or semicolon here is optional (but note
9818 * that the original Bourne shell only allowed NL).
9819 */
9820 if (lasttoken != TNL && lasttoken != TSEMI)
9821 tokpushback++;
9822 }
Eric Andersenc470f442003-07-28 09:56:35 +00009823 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009824 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009825 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009826 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009827 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009828 break;
9829 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009830 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009831 n1->type = NCASE;
9832 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009833 raise_error_unexpected_syntax(TWORD);
9834 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009835 n2->type = NARG;
9836 n2->narg.text = wordtext;
9837 n2->narg.backquote = backquotelist;
9838 n2->narg.next = NULL;
9839 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009840 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009841 } while (readtoken() == TNL);
9842 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009843 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009844 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009845 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009846 checkkwd = CHKNL | CHKKWD;
9847 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009848 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009849 if (lasttoken == TLP)
9850 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009851 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009852 cp->type = NCLIST;
9853 app = &cp->nclist.pattern;
9854 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009855 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009856 ap->type = NARG;
9857 ap->narg.text = wordtext;
9858 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009859 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009860 break;
9861 app = &ap->narg.next;
9862 readtoken();
9863 }
9864 ap->narg.next = NULL;
9865 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009866 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009867 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009868
Eric Andersenc470f442003-07-28 09:56:35 +00009869 cpp = &cp->nclist.next;
9870
9871 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009872 t = readtoken();
9873 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009874 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009875 raise_error_unexpected_syntax(TENDCASE);
9876 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009877 }
Eric Andersenc470f442003-07-28 09:56:35 +00009878 }
Eric Andersencb57d552001-06-28 07:25:16 +00009879 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009880 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009881 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009882 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009883 n1->type = NSUBSHELL;
9884 n1->nredir.n = list(0);
9885 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009886 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009887 break;
9888 case TBEGIN:
9889 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009890 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009891 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009892 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009893 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009894 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009895 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009896 }
9897
Eric Andersenc470f442003-07-28 09:56:35 +00009898 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009899 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009900
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009901 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009902 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009903 checkkwd = CHKKWD | CHKALIAS;
9904 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009905 while (readtoken() == TREDIR) {
9906 *rpp = n2 = redirnode;
9907 rpp = &n2->nfile.next;
9908 parsefname();
9909 }
9910 tokpushback++;
9911 *rpp = NULL;
9912 if (redir) {
9913 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009914 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009915 n2->type = NREDIR;
9916 n2->nredir.n = n1;
9917 n1 = n2;
9918 }
9919 n1->nredir.redirect = redir;
9920 }
Eric Andersencb57d552001-06-28 07:25:16 +00009921 return n1;
9922}
9923
Eric Andersencb57d552001-06-28 07:25:16 +00009924/*
9925 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9926 * is not NULL, read a here document. In the latter case, eofmark is the
9927 * word which marks the end of the document and striptabs is true if
9928 * leading tabs should be stripped from the document. The argument firstc
9929 * is the first character of the input token or document.
9930 *
9931 * Because C does not have internal subroutines, I have simulated them
9932 * using goto's to implement the subroutine linkage. The following macros
9933 * will run code that appears at the end of readtoken1.
9934 */
9935
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009936static int parsebackquote; /* nonzero if we are inside backquotes */
9937
Eric Andersen2870d962001-07-02 17:27:21 +00009938#define CHECKEND() {goto checkend; checkend_return:;}
9939#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9940#define PARSESUB() {goto parsesub; parsesub_return:;}
9941#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9942#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9943#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009944
9945static int
Eric Andersenc470f442003-07-28 09:56:35 +00009946readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009947{
Eric Andersencb57d552001-06-28 07:25:16 +00009948 int c = firstc;
9949 char *out;
9950 int len;
9951 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009952 struct nodelist *bqlist = 0;
9953 int quotef = 0;
9954 int dblquote = 0;
9955 int varnest = 0; /* levels of variables expansion */
9956 int arinest = 0; /* levels of arithmetic expansion */
9957 int parenlevel = 0; /* levels of parens in arithmetic */
9958 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9959 int oldstyle = 0;
9960 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009961#if __GNUC__
9962 /* Avoid longjmp clobbering */
9963 (void) &out;
9964 (void) &quotef;
9965 (void) &dblquote;
9966 (void) &varnest;
9967 (void) &arinest;
9968 (void) &parenlevel;
9969 (void) &dqvarnest;
9970 (void) &oldstyle;
9971 (void) &prevsyntax;
9972 (void) &syntax;
9973#endif
9974
9975 startlinno = plinno;
9976 dblquote = 0;
9977 if (syntax == DQSYNTAX)
9978 dblquote = 1;
9979 quotef = 0;
9980 bqlist = NULL;
9981 varnest = 0;
9982 arinest = 0;
9983 parenlevel = 0;
9984 dqvarnest = 0;
9985
9986 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009987 loop: { /* for each line, until end of word */
9988 CHECKEND(); /* set c to PEOF if at end of here document */
9989 for (;;) { /* until end of line or end of word */
9990 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009991 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009992 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009993 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009994 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009995 USTPUTC(c, out);
9996 plinno++;
9997 if (doprompt)
9998 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009999 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010000 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010001 case CWORD:
10002 USTPUTC(c, out);
10003 break;
10004 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010005 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010006 USTPUTC(CTLESC, out);
10007 USTPUTC(c, out);
10008 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010009 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010010 c = pgetc2();
10011 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010012 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010013 USTPUTC('\\', out);
10014 pungetc();
10015 } else if (c == '\n') {
10016 if (doprompt)
10017 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010018 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010019 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010020 c != '\\' && c != '`' &&
10021 c != '$' && (
10022 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010023 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010024 ) {
10025 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010026 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010027 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010028 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010029 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010030 USTPUTC(c, out);
10031 quotef++;
10032 }
10033 break;
10034 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010035 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010036 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010037 if (eofmark == NULL) {
10038 USTPUTC(CTLQUOTEMARK, out);
10039 }
Eric Andersencb57d552001-06-28 07:25:16 +000010040 break;
10041 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010042 syntax = DQSYNTAX;
10043 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010044 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010045 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010046 if (eofmark != NULL && arinest == 0
10047 && varnest == 0
10048 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010049 USTPUTC(c, out);
10050 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010051 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010052 syntax = BASESYNTAX;
10053 dblquote = 0;
10054 }
10055 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +000010056 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010057 }
10058 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010059 case CVAR: /* '$' */
10060 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010061 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010062 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010063 if (varnest > 0) {
10064 varnest--;
10065 if (dqvarnest > 0) {
10066 dqvarnest--;
10067 }
10068 USTPUTC(CTLENDVAR, out);
10069 } else {
10070 USTPUTC(c, out);
10071 }
10072 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010073#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010074 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010075 parenlevel++;
10076 USTPUTC(c, out);
10077 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010078 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010079 if (parenlevel > 0) {
10080 USTPUTC(c, out);
10081 --parenlevel;
10082 } else {
10083 if (pgetc() == ')') {
10084 if (--arinest == 0) {
10085 USTPUTC(CTLENDARI, out);
10086 syntax = prevsyntax;
10087 if (syntax == DQSYNTAX)
10088 dblquote = 1;
10089 else
10090 dblquote = 0;
10091 } else
10092 USTPUTC(')', out);
10093 } else {
10094 /*
10095 * unbalanced parens
10096 * (don't 2nd guess - no error)
10097 */
10098 pungetc();
10099 USTPUTC(')', out);
10100 }
10101 }
10102 break;
10103#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010104 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010105 PARSEBACKQOLD();
10106 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010107 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010108 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010109 case CIGN:
10110 break;
10111 default:
10112 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010113 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010114#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010115 if (c != PEOA)
10116#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010117 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010118
Eric Andersencb57d552001-06-28 07:25:16 +000010119 }
10120 c = pgetc_macro();
10121 }
10122 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010123 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010124#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010125 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010126 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010127#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010128 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010129 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010130 if (varnest != 0) {
10131 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010132 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010133 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010134 }
10135 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010136 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010137 out = stackblock();
10138 if (eofmark == NULL) {
10139 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010140 && quotef == 0
10141 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010142 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010143 PARSEREDIR();
10144 return lasttoken = TREDIR;
10145 } else {
10146 pungetc();
10147 }
10148 }
10149 quoteflag = quotef;
10150 backquotelist = bqlist;
10151 grabstackblock(len);
10152 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010153 lasttoken = TWORD;
10154 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010155/* end of readtoken routine */
10156
Eric Andersencb57d552001-06-28 07:25:16 +000010157/*
10158 * Check to see whether we are at the end of the here document. When this
10159 * is called, c is set to the first character of the next input line. If
10160 * we are at the end of the here document, this routine sets the c to PEOF.
10161 */
Eric Andersenc470f442003-07-28 09:56:35 +000010162checkend: {
10163 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010164#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010165 if (c == PEOA) {
10166 c = pgetc2();
10167 }
10168#endif
10169 if (striptabs) {
10170 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010171 c = pgetc2();
10172 }
Eric Andersenc470f442003-07-28 09:56:35 +000010173 }
10174 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010175 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010176 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010177
Eric Andersenc470f442003-07-28 09:56:35 +000010178 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010179 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010180 if (*p == '\n' && *q == '\0') {
10181 c = PEOF;
10182 plinno++;
10183 needprompt = doprompt;
10184 } else {
10185 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010186 }
10187 }
10188 }
10189 }
Eric Andersenc470f442003-07-28 09:56:35 +000010190 goto checkend_return;
10191}
Eric Andersencb57d552001-06-28 07:25:16 +000010192
Eric Andersencb57d552001-06-28 07:25:16 +000010193/*
10194 * Parse a redirection operator. The variable "out" points to a string
10195 * specifying the fd to be redirected. The variable "c" contains the
10196 * first character of the redirection operator.
10197 */
Eric Andersenc470f442003-07-28 09:56:35 +000010198parseredir: {
10199 char fd = *out;
10200 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010201
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010202 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010203 if (c == '>') {
10204 np->nfile.fd = 1;
10205 c = pgetc();
10206 if (c == '>')
10207 np->type = NAPPEND;
10208 else if (c == '|')
10209 np->type = NCLOBBER;
10210 else if (c == '&')
10211 np->type = NTOFD;
10212 else {
10213 np->type = NTO;
10214 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010215 }
Eric Andersenc470f442003-07-28 09:56:35 +000010216 } else { /* c == '<' */
10217 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010218 c = pgetc();
10219 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010220 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010221 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010222 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010223 np->nfile.fd = 0;
10224 }
10225 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010226 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010227 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010228 c = pgetc();
10229 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010230 heredoc->striptabs = 1;
10231 } else {
10232 heredoc->striptabs = 0;
10233 pungetc();
10234 }
10235 break;
10236
10237 case '&':
10238 np->type = NFROMFD;
10239 break;
10240
10241 case '>':
10242 np->type = NFROMTO;
10243 break;
10244
10245 default:
10246 np->type = NFROM;
10247 pungetc();
10248 break;
10249 }
Eric Andersencb57d552001-06-28 07:25:16 +000010250 }
Eric Andersenc470f442003-07-28 09:56:35 +000010251 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010252 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010253 redirnode = np;
10254 goto parseredir_return;
10255}
Eric Andersencb57d552001-06-28 07:25:16 +000010256
Eric Andersencb57d552001-06-28 07:25:16 +000010257/*
10258 * Parse a substitution. At this point, we have read the dollar sign
10259 * and nothing else.
10260 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010261
10262/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10263 * (assuming ascii char codes, as the original implementation did) */
10264#define is_special(c) \
10265 ((((unsigned int)c) - 33 < 32) \
10266 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010267parsesub: {
10268 int subtype;
10269 int typeloc;
10270 int flags;
10271 char *p;
10272 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010273
Eric Andersenc470f442003-07-28 09:56:35 +000010274 c = pgetc();
10275 if (
10276 c <= PEOA_OR_PEOF ||
10277 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10278 ) {
10279 USTPUTC('$', out);
10280 pungetc();
10281 } else if (c == '(') { /* $(command) or $((arith)) */
10282 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010283#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010284 PARSEARITH();
10285#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010286 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010287#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010288 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010289 pungetc();
10290 PARSEBACKQNEW();
10291 }
10292 } else {
10293 USTPUTC(CTLVAR, out);
10294 typeloc = out - (char *)stackblock();
10295 USTPUTC(VSNORMAL, out);
10296 subtype = VSNORMAL;
10297 if (c == '{') {
10298 c = pgetc();
10299 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010300 c = pgetc();
10301 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010302 c = '#';
10303 else
10304 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010305 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010306 subtype = 0;
10307 }
10308 if (c > PEOA_OR_PEOF && is_name(c)) {
10309 do {
10310 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010311 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010312 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010313 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010314 do {
10315 STPUTC(c, out);
10316 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010317 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010318 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010319 USTPUTC(c, out);
10320 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010321 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010322 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010323
Eric Andersenc470f442003-07-28 09:56:35 +000010324 STPUTC('=', out);
10325 flags = 0;
10326 if (subtype == 0) {
10327 switch (c) {
10328 case ':':
10329 flags = VSNUL;
10330 c = pgetc();
10331 /*FALLTHROUGH*/
10332 default:
10333 p = strchr(types, c);
10334 if (p == NULL)
10335 goto badsub;
10336 subtype = p - types + VSNORMAL;
10337 break;
10338 case '%':
10339 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010340 {
10341 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010342 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010343 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010344 c = pgetc();
10345 if (c == cc)
10346 subtype++;
10347 else
10348 pungetc();
10349 break;
10350 }
10351 }
Eric Andersenc470f442003-07-28 09:56:35 +000010352 } else {
10353 pungetc();
10354 }
10355 if (dblquote || arinest)
10356 flags |= VSQUOTE;
10357 *((char *)stackblock() + typeloc) = subtype | flags;
10358 if (subtype != VSNORMAL) {
10359 varnest++;
10360 if (dblquote || arinest) {
10361 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010362 }
10363 }
10364 }
Eric Andersenc470f442003-07-28 09:56:35 +000010365 goto parsesub_return;
10366}
Eric Andersencb57d552001-06-28 07:25:16 +000010367
Eric Andersencb57d552001-06-28 07:25:16 +000010368/*
10369 * Called to parse command substitutions. Newstyle is set if the command
10370 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10371 * list of commands (passed by reference), and savelen is the number of
10372 * characters on the top of the stack which must be preserved.
10373 */
Eric Andersenc470f442003-07-28 09:56:35 +000010374parsebackq: {
10375 struct nodelist **nlpp;
10376 int savepbq;
10377 union node *n;
10378 char *volatile str;
10379 struct jmploc jmploc;
10380 struct jmploc *volatile savehandler;
10381 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010382 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010383#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010384 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010385#endif
10386
Eric Andersenc470f442003-07-28 09:56:35 +000010387 savepbq = parsebackquote;
10388 if (setjmp(jmploc.loc)) {
10389 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010390 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010391 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010392 exception_handler = savehandler;
10393 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010394 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010395 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010396 str = NULL;
10397 savelen = out - (char *)stackblock();
10398 if (savelen > 0) {
10399 str = ckmalloc(savelen);
10400 memcpy(str, stackblock(), savelen);
10401 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010402 savehandler = exception_handler;
10403 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010404 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010405 if (oldstyle) {
10406 /* We must read until the closing backquote, giving special
10407 treatment to some slashes, and then push the string and
10408 reread it as input, interpreting it normally. */
10409 char *pout;
10410 int pc;
10411 size_t psavelen;
10412 char *pstr;
10413
10414
10415 STARTSTACKSTR(pout);
10416 for (;;) {
10417 if (needprompt) {
10418 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010419 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010420 pc = pgetc();
10421 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010422 case '`':
10423 goto done;
10424
10425 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010426 pc = pgetc();
10427 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010428 plinno++;
10429 if (doprompt)
10430 setprompt(2);
10431 /*
10432 * If eating a newline, avoid putting
10433 * the newline into the new character
10434 * stream (via the STPUTC after the
10435 * switch).
10436 */
10437 continue;
10438 }
10439 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010440 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010441 STPUTC('\\', pout);
10442 if (pc > PEOA_OR_PEOF) {
10443 break;
10444 }
10445 /* fall through */
10446
10447 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010448#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010449 case PEOA:
10450#endif
10451 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010452 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010453
10454 case '\n':
10455 plinno++;
10456 needprompt = doprompt;
10457 break;
10458
10459 default:
10460 break;
10461 }
10462 STPUTC(pc, pout);
10463 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010464 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010465 STPUTC('\0', pout);
10466 psavelen = pout - (char *)stackblock();
10467 if (psavelen > 0) {
10468 pstr = grabstackstr(pout);
10469 setinputstring(pstr);
10470 }
10471 }
10472 nlpp = &bqlist;
10473 while (*nlpp)
10474 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010475 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010476 (*nlpp)->next = NULL;
10477 parsebackquote = oldstyle;
10478
10479 if (oldstyle) {
10480 saveprompt = doprompt;
10481 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010482 }
10483
Eric Andersenc470f442003-07-28 09:56:35 +000010484 n = list(2);
10485
10486 if (oldstyle)
10487 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010488 else if (readtoken() != TRP)
10489 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010490
10491 (*nlpp)->n = n;
10492 if (oldstyle) {
10493 /*
10494 * Start reading from old file again, ignoring any pushed back
10495 * tokens left from the backquote parsing
10496 */
10497 popfile();
10498 tokpushback = 0;
10499 }
10500 while (stackblocksize() <= savelen)
10501 growstackblock();
10502 STARTSTACKSTR(out);
10503 if (str) {
10504 memcpy(out, str, savelen);
10505 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010506 INT_OFF;
10507 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010508 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010509 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010510 }
10511 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010512 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010513 if (arinest || dblquote)
10514 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10515 else
10516 USTPUTC(CTLBACKQ, out);
10517 if (oldstyle)
10518 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010519 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010520}
10521
Denis Vlasenko131ae172007-02-18 13:00:19 +000010522#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010523/*
10524 * Parse an arithmetic expansion (indicate start of one and set state)
10525 */
Eric Andersenc470f442003-07-28 09:56:35 +000010526parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010527 if (++arinest == 1) {
10528 prevsyntax = syntax;
10529 syntax = ARISYNTAX;
10530 USTPUTC(CTLARI, out);
10531 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010532 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010533 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010534 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010535 } else {
10536 /*
10537 * we collapse embedded arithmetic expansion to
10538 * parenthesis, which should be equivalent
10539 */
10540 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010541 }
Eric Andersenc470f442003-07-28 09:56:35 +000010542 goto parsearith_return;
10543}
10544#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010545
Eric Andersenc470f442003-07-28 09:56:35 +000010546} /* end of readtoken */
10547
Eric Andersencb57d552001-06-28 07:25:16 +000010548/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010549 * Read the next input token.
10550 * If the token is a word, we set backquotelist to the list of cmds in
10551 * backquotes. We set quoteflag to true if any part of the word was
10552 * quoted.
10553 * If the token is TREDIR, then we set redirnode to a structure containing
10554 * the redirection.
10555 * In all cases, the variable startlinno is set to the number of the line
10556 * on which the token starts.
10557 *
10558 * [Change comment: here documents and internal procedures]
10559 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10560 * word parsing code into a separate routine. In this case, readtoken
10561 * doesn't need to have any internal procedures, but parseword does.
10562 * We could also make parseoperator in essence the main routine, and
10563 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010564 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010565#define NEW_xxreadtoken
10566#ifdef NEW_xxreadtoken
10567/* singles must be first! */
10568static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010569
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010570static const char xxreadtoken_tokens[] = {
10571 TNL, TLP, TRP, /* only single occurrence allowed */
10572 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10573 TEOF, /* corresponds to trailing nul */
10574 TAND, TOR, TENDCASE, /* if double occurrence */
10575};
10576
10577#define xxreadtoken_doubles \
10578 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10579#define xxreadtoken_singles \
10580 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10581
10582static int
10583xxreadtoken(void)
10584{
10585 int c;
10586
10587 if (tokpushback) {
10588 tokpushback = 0;
10589 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010590 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010591 if (needprompt) {
10592 setprompt(2);
10593 }
10594 startlinno = plinno;
10595 for (;;) { /* until token or start of word found */
10596 c = pgetc_macro();
10597
10598 if ((c != ' ') && (c != '\t')
10599#if ENABLE_ASH_ALIAS
10600 && (c != PEOA)
10601#endif
10602 ) {
10603 if (c == '#') {
10604 while ((c = pgetc()) != '\n' && c != PEOF);
10605 pungetc();
10606 } else if (c == '\\') {
10607 if (pgetc() != '\n') {
10608 pungetc();
10609 goto READTOKEN1;
10610 }
10611 startlinno = ++plinno;
10612 if (doprompt)
10613 setprompt(2);
10614 } else {
10615 const char *p
10616 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10617
10618 if (c != PEOF) {
10619 if (c == '\n') {
10620 plinno++;
10621 needprompt = doprompt;
10622 }
10623
10624 p = strchr(xxreadtoken_chars, c);
10625 if (p == NULL) {
10626 READTOKEN1:
10627 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10628 }
10629
10630 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10631 if (pgetc() == *p) { /* double occurrence? */
10632 p += xxreadtoken_doubles + 1;
10633 } else {
10634 pungetc();
10635 }
10636 }
10637 }
10638 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10639 }
10640 }
10641 } /* for */
10642}
10643#else
10644#define RETURN(token) return lasttoken = token
10645static int
10646xxreadtoken(void)
10647{
10648 int c;
10649
10650 if (tokpushback) {
10651 tokpushback = 0;
10652 return lasttoken;
10653 }
10654 if (needprompt) {
10655 setprompt(2);
10656 }
10657 startlinno = plinno;
10658 for (;;) { /* until token or start of word found */
10659 c = pgetc_macro();
10660 switch (c) {
10661 case ' ': case '\t':
10662#if ENABLE_ASH_ALIAS
10663 case PEOA:
10664#endif
10665 continue;
10666 case '#':
10667 while ((c = pgetc()) != '\n' && c != PEOF);
10668 pungetc();
10669 continue;
10670 case '\\':
10671 if (pgetc() == '\n') {
10672 startlinno = ++plinno;
10673 if (doprompt)
10674 setprompt(2);
10675 continue;
10676 }
10677 pungetc();
10678 goto breakloop;
10679 case '\n':
10680 plinno++;
10681 needprompt = doprompt;
10682 RETURN(TNL);
10683 case PEOF:
10684 RETURN(TEOF);
10685 case '&':
10686 if (pgetc() == '&')
10687 RETURN(TAND);
10688 pungetc();
10689 RETURN(TBACKGND);
10690 case '|':
10691 if (pgetc() == '|')
10692 RETURN(TOR);
10693 pungetc();
10694 RETURN(TPIPE);
10695 case ';':
10696 if (pgetc() == ';')
10697 RETURN(TENDCASE);
10698 pungetc();
10699 RETURN(TSEMI);
10700 case '(':
10701 RETURN(TLP);
10702 case ')':
10703 RETURN(TRP);
10704 default:
10705 goto breakloop;
10706 }
10707 }
10708 breakloop:
10709 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10710#undef RETURN
10711}
10712#endif /* NEW_xxreadtoken */
10713
10714static int
10715readtoken(void)
10716{
10717 int t;
10718#if DEBUG
10719 int alreadyseen = tokpushback;
10720#endif
10721
10722#if ENABLE_ASH_ALIAS
10723 top:
10724#endif
10725
10726 t = xxreadtoken();
10727
10728 /*
10729 * eat newlines
10730 */
10731 if (checkkwd & CHKNL) {
10732 while (t == TNL) {
10733 parseheredoc();
10734 t = xxreadtoken();
10735 }
10736 }
10737
10738 if (t != TWORD || quoteflag) {
10739 goto out;
10740 }
10741
10742 /*
10743 * check for keywords
10744 */
10745 if (checkkwd & CHKKWD) {
10746 const char *const *pp;
10747
10748 pp = findkwd(wordtext);
10749 if (pp) {
10750 lasttoken = t = pp - tokname_array;
10751 TRACE(("keyword %s recognized\n", tokname(t)));
10752 goto out;
10753 }
10754 }
10755
10756 if (checkkwd & CHKALIAS) {
10757#if ENABLE_ASH_ALIAS
10758 struct alias *ap;
10759 ap = lookupalias(wordtext, 1);
10760 if (ap != NULL) {
10761 if (*ap->val) {
10762 pushstring(ap->val, ap);
10763 }
10764 goto top;
10765 }
10766#endif
10767 }
10768 out:
10769 checkkwd = 0;
10770#if DEBUG
10771 if (!alreadyseen)
10772 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10773 else
10774 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10775#endif
10776 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010777}
10778
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010779static char
10780peektoken(void)
10781{
10782 int t;
10783
10784 t = readtoken();
10785 tokpushback++;
10786 return tokname_array[t][0];
10787}
Eric Andersencb57d552001-06-28 07:25:16 +000010788
10789/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010790 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10791 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010792 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010793static union node *
10794parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010795{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010796 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010797
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010798 tokpushback = 0;
10799 doprompt = interact;
10800 if (doprompt)
10801 setprompt(doprompt);
10802 needprompt = 0;
10803 t = readtoken();
10804 if (t == TEOF)
10805 return NEOF;
10806 if (t == TNL)
10807 return NULL;
10808 tokpushback++;
10809 return list(1);
10810}
10811
10812/*
10813 * Input any here documents.
10814 */
10815static void
10816parseheredoc(void)
10817{
10818 struct heredoc *here;
10819 union node *n;
10820
10821 here = heredoclist;
10822 heredoclist = 0;
10823
10824 while (here) {
10825 if (needprompt) {
10826 setprompt(2);
10827 }
10828 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10829 here->eofmark, here->striptabs);
10830 n = stalloc(sizeof(struct narg));
10831 n->narg.type = NARG;
10832 n->narg.next = NULL;
10833 n->narg.text = wordtext;
10834 n->narg.backquote = backquotelist;
10835 here->here->nhere.doc = n;
10836 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010837 }
Eric Andersencb57d552001-06-28 07:25:16 +000010838}
10839
10840
10841/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010842 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010843 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010844#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010845static const char *
10846expandstr(const char *ps)
10847{
10848 union node n;
10849
10850 /* XXX Fix (char *) cast. */
10851 setinputstring((char *)ps);
10852 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10853 popfile();
10854
10855 n.narg.type = NARG;
10856 n.narg.next = NULL;
10857 n.narg.text = wordtext;
10858 n.narg.backquote = backquotelist;
10859
10860 expandarg(&n, NULL, 0);
10861 return stackblock();
10862}
10863#endif
10864
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010865/*
10866 * Execute a command or commands contained in a string.
10867 */
10868static int
10869evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010870{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010871 union node *n;
10872 struct stackmark smark;
10873 int skip;
10874
10875 setinputstring(s);
10876 setstackmark(&smark);
10877
10878 skip = 0;
10879 while ((n = parsecmd(0)) != NEOF) {
10880 evaltree(n, 0);
10881 popstackmark(&smark);
10882 skip = evalskip;
10883 if (skip)
10884 break;
10885 }
10886 popfile();
10887
10888 skip &= mask;
10889 evalskip = skip;
10890 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010891}
10892
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010893/*
10894 * The eval command.
10895 */
10896static int
10897evalcmd(int argc, char **argv)
10898{
10899 char *p;
10900 char *concat;
10901 char **ap;
10902
10903 if (argc > 1) {
10904 p = argv[1];
10905 if (argc > 2) {
10906 STARTSTACKSTR(concat);
10907 ap = argv + 2;
10908 for (;;) {
10909 concat = stack_putstr(p, concat);
10910 p = *ap++;
10911 if (p == NULL)
10912 break;
10913 STPUTC(' ', concat);
10914 }
10915 STPUTC('\0', concat);
10916 p = grabstackstr(concat);
10917 }
10918 evalstring(p, ~SKIPEVAL);
10919
10920 }
10921 return exitstatus;
10922}
10923
10924/*
10925 * Read and execute commands. "Top" is nonzero for the top level command
10926 * loop; it turns on prompting if the shell is interactive.
10927 */
10928static int
10929cmdloop(int top)
10930{
10931 union node *n;
10932 struct stackmark smark;
10933 int inter;
10934 int numeof = 0;
10935
10936 TRACE(("cmdloop(%d) called\n", top));
10937 for (;;) {
10938 int skip;
10939
10940 setstackmark(&smark);
10941#if JOBS
10942 if (jobctl)
10943 showjobs(stderr, SHOW_CHANGED);
10944#endif
10945 inter = 0;
10946 if (iflag && top) {
10947 inter++;
10948#if ENABLE_ASH_MAIL
10949 chkmail();
10950#endif
10951 }
10952 n = parsecmd(inter);
10953 /* showtree(n); DEBUG */
10954 if (n == NEOF) {
10955 if (!top || numeof >= 50)
10956 break;
10957 if (!stoppedjobs()) {
10958 if (!Iflag)
10959 break;
10960 out2str("\nUse \"exit\" to leave shell.\n");
10961 }
10962 numeof++;
10963 } else if (nflag == 0) {
10964 job_warning = (job_warning == 2) ? 1 : 0;
10965 numeof = 0;
10966 evaltree(n, 0);
10967 }
10968 popstackmark(&smark);
10969 skip = evalskip;
10970
10971 if (skip) {
10972 evalskip = 0;
10973 return skip & SKIPEVAL;
10974 }
10975 }
10976 return 0;
10977}
10978
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010979/*
10980 * Take commands from a file. To be compatible we should do a path
10981 * search for the file, which is necessary to find sub-commands.
10982 */
10983static char *
10984find_dot_file(char *name)
10985{
10986 char *fullname;
10987 const char *path = pathval();
10988 struct stat statb;
10989
10990 /* don't try this for absolute or relative paths */
10991 if (strchr(name, '/'))
10992 return name;
10993
10994 while ((fullname = padvance(&path, name)) != NULL) {
10995 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10996 /*
10997 * Don't bother freeing here, since it will
10998 * be freed by the caller.
10999 */
11000 return fullname;
11001 }
11002 stunalloc(fullname);
11003 }
11004
11005 /* not found in the PATH */
11006 ash_msg_and_raise_error("%s: not found", name);
11007 /* NOTREACHED */
11008}
11009
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011010static int
11011dotcmd(int argc, char **argv)
11012{
11013 struct strlist *sp;
11014 volatile struct shparam saveparam;
11015 int status = 0;
11016
11017 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011018 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011019
11020 if (argc >= 2) { /* That's what SVR2 does */
11021 char *fullname;
11022
11023 fullname = find_dot_file(argv[1]);
11024
11025 if (argc > 2) {
11026 saveparam = shellparam;
11027 shellparam.malloc = 0;
11028 shellparam.nparam = argc - 2;
11029 shellparam.p = argv + 2;
11030 };
11031
11032 setinputfile(fullname, INPUT_PUSH_FILE);
11033 commandname = fullname;
11034 cmdloop(0);
11035 popfile();
11036
11037 if (argc > 2) {
11038 freeparam(&shellparam);
11039 shellparam = saveparam;
11040 };
11041 status = exitstatus;
11042 }
11043 return status;
11044}
11045
11046static int
11047exitcmd(int argc, char **argv)
11048{
11049 if (stoppedjobs())
11050 return 0;
11051 if (argc > 1)
11052 exitstatus = number(argv[1]);
11053 raise_exception(EXEXIT);
11054 /* NOTREACHED */
11055}
11056
11057#if ENABLE_ASH_BUILTIN_ECHO
11058static int
11059echocmd(int argc, char **argv)
11060{
11061 return bb_echo(argv);
11062}
11063#endif
11064
11065#if ENABLE_ASH_BUILTIN_TEST
11066static int
11067testcmd(int argc, char **argv)
11068{
11069 return bb_test(argc, argv);
11070}
11071#endif
11072
11073/*
11074 * Read a file containing shell functions.
11075 */
11076static void
11077readcmdfile(char *name)
11078{
11079 setinputfile(name, INPUT_PUSH_FILE);
11080 cmdloop(0);
11081 popfile();
11082}
11083
11084
Denis Vlasenkocc571512007-02-23 21:10:35 +000011085/* ============ find_command inplementation */
11086
11087/*
11088 * Resolve a command name. If you change this routine, you may have to
11089 * change the shellexec routine as well.
11090 */
11091static void
11092find_command(char *name, struct cmdentry *entry, int act, const char *path)
11093{
11094 struct tblentry *cmdp;
11095 int idx;
11096 int prev;
11097 char *fullname;
11098 struct stat statb;
11099 int e;
11100 int updatetbl;
11101 struct builtincmd *bcmd;
11102
11103 /* If name contains a slash, don't use PATH or hash table */
11104 if (strchr(name, '/') != NULL) {
11105 entry->u.index = -1;
11106 if (act & DO_ABS) {
11107 while (stat(name, &statb) < 0) {
11108#ifdef SYSV
11109 if (errno == EINTR)
11110 continue;
11111#endif
11112 entry->cmdtype = CMDUNKNOWN;
11113 return;
11114 }
11115 }
11116 entry->cmdtype = CMDNORMAL;
11117 return;
11118 }
11119
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011120#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenkocc571512007-02-23 21:10:35 +000011121 if (find_applet_by_name(name)) {
11122 entry->cmdtype = CMDNORMAL;
11123 entry->u.index = -1;
11124 return;
11125 }
Denis Vlasenko29e31dd2007-03-03 23:12:17 +000011126#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000011127
11128 updatetbl = (path == pathval());
11129 if (!updatetbl) {
11130 act |= DO_ALTPATH;
11131 if (strstr(path, "%builtin") != NULL)
11132 act |= DO_ALTBLTIN;
11133 }
11134
11135 /* If name is in the table, check answer will be ok */
11136 cmdp = cmdlookup(name, 0);
11137 if (cmdp != NULL) {
11138 int bit;
11139
11140 switch (cmdp->cmdtype) {
11141 default:
11142#if DEBUG
11143 abort();
11144#endif
11145 case CMDNORMAL:
11146 bit = DO_ALTPATH;
11147 break;
11148 case CMDFUNCTION:
11149 bit = DO_NOFUNC;
11150 break;
11151 case CMDBUILTIN:
11152 bit = DO_ALTBLTIN;
11153 break;
11154 }
11155 if (act & bit) {
11156 updatetbl = 0;
11157 cmdp = NULL;
11158 } else if (cmdp->rehash == 0)
11159 /* if not invalidated by cd, we're done */
11160 goto success;
11161 }
11162
11163 /* If %builtin not in path, check for builtin next */
11164 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011165 if (bcmd) {
11166 if (IS_BUILTIN_REGULAR(bcmd))
11167 goto builtin_success;
11168 if (act & DO_ALTPATH) {
11169 if (!(act & DO_ALTBLTIN))
11170 goto builtin_success;
11171 } else if (builtinloc <= 0) {
11172 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011173 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011174 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011175
11176 /* We have to search path. */
11177 prev = -1; /* where to start */
11178 if (cmdp && cmdp->rehash) { /* doing a rehash */
11179 if (cmdp->cmdtype == CMDBUILTIN)
11180 prev = builtinloc;
11181 else
11182 prev = cmdp->param.index;
11183 }
11184
11185 e = ENOENT;
11186 idx = -1;
11187 loop:
11188 while ((fullname = padvance(&path, name)) != NULL) {
11189 stunalloc(fullname);
11190 idx++;
11191 if (pathopt) {
11192 if (prefix(pathopt, "builtin")) {
11193 if (bcmd)
11194 goto builtin_success;
11195 continue;
11196 } else if (!(act & DO_NOFUNC) &&
11197 prefix(pathopt, "func")) {
11198 /* handled below */
11199 } else {
11200 /* ignore unimplemented options */
11201 continue;
11202 }
11203 }
11204 /* if rehash, don't redo absolute path names */
11205 if (fullname[0] == '/' && idx <= prev) {
11206 if (idx < prev)
11207 continue;
11208 TRACE(("searchexec \"%s\": no change\n", name));
11209 goto success;
11210 }
11211 while (stat(fullname, &statb) < 0) {
11212#ifdef SYSV
11213 if (errno == EINTR)
11214 continue;
11215#endif
11216 if (errno != ENOENT && errno != ENOTDIR)
11217 e = errno;
11218 goto loop;
11219 }
11220 e = EACCES; /* if we fail, this will be the error */
11221 if (!S_ISREG(statb.st_mode))
11222 continue;
11223 if (pathopt) { /* this is a %func directory */
11224 stalloc(strlen(fullname) + 1);
11225 readcmdfile(fullname);
11226 cmdp = cmdlookup(name, 0);
11227 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11228 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11229 stunalloc(fullname);
11230 goto success;
11231 }
11232 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11233 if (!updatetbl) {
11234 entry->cmdtype = CMDNORMAL;
11235 entry->u.index = idx;
11236 return;
11237 }
11238 INT_OFF;
11239 cmdp = cmdlookup(name, 1);
11240 cmdp->cmdtype = CMDNORMAL;
11241 cmdp->param.index = idx;
11242 INT_ON;
11243 goto success;
11244 }
11245
11246 /* We failed. If there was an entry for this command, delete it */
11247 if (cmdp && updatetbl)
11248 delete_cmd_entry();
11249 if (act & DO_ERR)
11250 ash_msg("%s: %s", name, errmsg(e, "not found"));
11251 entry->cmdtype = CMDUNKNOWN;
11252 return;
11253
11254 builtin_success:
11255 if (!updatetbl) {
11256 entry->cmdtype = CMDBUILTIN;
11257 entry->u.cmd = bcmd;
11258 return;
11259 }
11260 INT_OFF;
11261 cmdp = cmdlookup(name, 1);
11262 cmdp->cmdtype = CMDBUILTIN;
11263 cmdp->param.cmd = bcmd;
11264 INT_ON;
11265 success:
11266 cmdp->rehash = 0;
11267 entry->cmdtype = cmdp->cmdtype;
11268 entry->u = cmdp->param;
11269}
11270
11271
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011272/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011273
Eric Andersencb57d552001-06-28 07:25:16 +000011274/*
Eric Andersencb57d552001-06-28 07:25:16 +000011275 * The trap builtin.
11276 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011277static int
Eric Andersenc470f442003-07-28 09:56:35 +000011278trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011279{
11280 char *action;
11281 char **ap;
11282 int signo;
11283
Eric Andersenc470f442003-07-28 09:56:35 +000011284 nextopt(nullstr);
11285 ap = argptr;
11286 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011287 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011288 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011289 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011290
Rob Landleyc9c1a412006-07-12 19:17:55 +000011291 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011292 out1fmt("trap -- %s %s\n",
11293 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011294 }
11295 }
11296 return 0;
11297 }
Eric Andersenc470f442003-07-28 09:56:35 +000011298 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011299 action = NULL;
11300 else
11301 action = *ap++;
11302 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011303 signo = get_signum(*ap);
11304 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011305 ash_msg_and_raise_error("%s: bad trap", *ap);
11306 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011307 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011308 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011309 action = NULL;
11310 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011311 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011312 }
Eric Andersenc470f442003-07-28 09:56:35 +000011313 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011314 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011315 trap[signo] = action;
11316 if (signo != 0)
11317 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011318 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011319 ap++;
11320 }
11321 return 0;
11322}
11323
Eric Andersenc470f442003-07-28 09:56:35 +000011324
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011325/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011326
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011327#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011328/*
11329 * Lists available builtins
11330 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011331static int
11332helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011333{
11334 int col, i;
11335
11336 out1fmt("\nBuilt-in commands:\n-------------------\n");
11337 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11338 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011339 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011340 if (col > 60) {
11341 out1fmt("\n");
11342 col = 0;
11343 }
11344 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011345#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011346 for (i = 0; i < NUM_APPLETS; i++) {
11347 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11348 if (col > 60) {
11349 out1fmt("\n");
11350 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011351 }
11352 }
11353#endif
11354 out1fmt("\n\n");
11355 return EXIT_SUCCESS;
11356}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011357#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011358
Eric Andersencb57d552001-06-28 07:25:16 +000011359/*
Eric Andersencb57d552001-06-28 07:25:16 +000011360 * The export and readonly commands.
11361 */
Eric Andersenc470f442003-07-28 09:56:35 +000011362static int
11363exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011364{
11365 struct var *vp;
11366 char *name;
11367 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011368 char **aptr;
11369 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011370
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011371 if (nextopt("p") != 'p') {
11372 aptr = argptr;
11373 name = *aptr;
11374 if (name) {
11375 do {
11376 p = strchr(name, '=');
11377 if (p != NULL) {
11378 p++;
11379 } else {
11380 vp = *findvar(hashvar(name), name);
11381 if (vp) {
11382 vp->flags |= flag;
11383 continue;
11384 }
Eric Andersencb57d552001-06-28 07:25:16 +000011385 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011386 setvar(name, p, flag);
11387 } while ((name = *++aptr) != NULL);
11388 return 0;
11389 }
Eric Andersencb57d552001-06-28 07:25:16 +000011390 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011391 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011392 return 0;
11393}
11394
Eric Andersencb57d552001-06-28 07:25:16 +000011395/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011396 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011397 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011398static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011399unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011400{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011401 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011402
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011403 cmdp = cmdlookup(name, 0);
11404 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11405 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011406}
11407
Eric Andersencb57d552001-06-28 07:25:16 +000011408/*
Eric Andersencb57d552001-06-28 07:25:16 +000011409 * The unset builtin command. We unset the function before we unset the
11410 * variable to allow a function to be unset when there is a readonly variable
11411 * with the same name.
11412 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011413static int
Eric Andersenc470f442003-07-28 09:56:35 +000011414unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011415{
11416 char **ap;
11417 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011418 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011419 int ret = 0;
11420
11421 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011422 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011423 }
Eric Andersencb57d552001-06-28 07:25:16 +000011424
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011425 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011426 if (flag != 'f') {
11427 i = unsetvar(*ap);
11428 ret |= i;
11429 if (!(i & 2))
11430 continue;
11431 }
11432 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011433 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011434 }
Eric Andersenc470f442003-07-28 09:56:35 +000011435 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011436}
11437
11438
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011439/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011440
Eric Andersenc470f442003-07-28 09:56:35 +000011441#include <sys/times.h>
11442
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011443static const unsigned char timescmd_str[] = {
11444 ' ', offsetof(struct tms, tms_utime),
11445 '\n', offsetof(struct tms, tms_stime),
11446 ' ', offsetof(struct tms, tms_cutime),
11447 '\n', offsetof(struct tms, tms_cstime),
11448 0
11449};
Eric Andersencb57d552001-06-28 07:25:16 +000011450
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011451static int
11452timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011453{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011454 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011455 const unsigned char *p;
11456 struct tms buf;
11457
11458 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011459 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011460
11461 p = timescmd_str;
11462 do {
11463 t = *(clock_t *)(((char *) &buf) + p[1]);
11464 s = t / clk_tck;
11465 out1fmt("%ldm%ld.%.3lds%c",
11466 s/60, s%60,
11467 ((t - s * clk_tck) * 1000) / clk_tck,
11468 p[0]);
11469 } while (*(p += 2));
11470
Eric Andersencb57d552001-06-28 07:25:16 +000011471 return 0;
11472}
11473
Denis Vlasenko131ae172007-02-18 13:00:19 +000011474#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011475static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011476dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011477{
Eric Andersened9ecf72004-06-22 08:29:45 +000011478 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011479 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011480
Denis Vlasenkob012b102007-02-19 22:43:01 +000011481 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011482 result = arith(s, &errcode);
11483 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011484 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011485 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011486 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011487 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011488 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011489 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011490 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011491 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011492 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011493
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011494 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011495}
Eric Andersenc470f442003-07-28 09:56:35 +000011496
Eric Andersenc470f442003-07-28 09:56:35 +000011497/*
Eric Andersen90898442003-08-06 11:20:52 +000011498 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11499 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11500 *
11501 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011502 */
11503static int
Eric Andersen90898442003-08-06 11:20:52 +000011504letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011505{
Eric Andersenc470f442003-07-28 09:56:35 +000011506 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011507 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011508
Eric Andersen90898442003-08-06 11:20:52 +000011509 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011510 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011511 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011512 for (ap = argv + 1; *ap; ap++) {
11513 i = dash_arith(*ap);
11514 }
Eric Andersenc470f442003-07-28 09:56:35 +000011515
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011516 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011517}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011518#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011519
Eric Andersenc470f442003-07-28 09:56:35 +000011520
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011521/* ============ miscbltin.c
11522 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011523 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011524 */
11525
11526#undef rflag
11527
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011528#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011529typedef enum __rlimit_resource rlim_t;
11530#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011531
Eric Andersenc470f442003-07-28 09:56:35 +000011532/*
11533 * The read builtin. The -e option causes backslashes to escape the
11534 * following character.
11535 *
11536 * This uses unbuffered input, which may be avoidable in some cases.
11537 */
Eric Andersenc470f442003-07-28 09:56:35 +000011538static int
11539readcmd(int argc, char **argv)
11540{
11541 char **ap;
11542 int backslash;
11543 char c;
11544 int rflag;
11545 char *prompt;
11546 const char *ifs;
11547 char *p;
11548 int startword;
11549 int status;
11550 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011551#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011552 int nch_flag = 0;
11553 int nchars = 0;
11554 int silent = 0;
11555 struct termios tty, old_tty;
11556#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011557#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011558 fd_set set;
11559 struct timeval ts;
11560
11561 ts.tv_sec = ts.tv_usec = 0;
11562#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011563
11564 rflag = 0;
11565 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011566#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011567 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011568#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011569 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011570#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011571 while ((i = nextopt("p:rt:")) != '\0')
11572#else
11573 while ((i = nextopt("p:r")) != '\0')
11574#endif
11575 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011576 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011577 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011578 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011579 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011580#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011581 case 'n':
11582 nchars = strtol(optionarg, &p, 10);
11583 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011584 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011585 nch_flag = (nchars > 0);
11586 break;
11587 case 's':
11588 silent = 1;
11589 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011590#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011591#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011592 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011593 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011594 ts.tv_usec = 0;
11595 if (*p == '.') {
11596 char *p2;
11597 if (*++p) {
11598 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011599 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011600 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011601 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011602 scale = p2 - p;
11603 /* normalize to usec */
11604 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011605 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011606 while (scale++ < 6)
11607 ts.tv_usec *= 10;
11608 }
11609 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011610 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011611 }
11612 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011613 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011614 break;
11615#endif
11616 case 'r':
11617 rflag = 1;
11618 break;
11619 default:
11620 break;
11621 }
Eric Andersenc470f442003-07-28 09:56:35 +000011622 }
11623 if (prompt && isatty(0)) {
11624 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011625 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011626 ap = argptr;
11627 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011628 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011629 ifs = bltinlookup("IFS");
11630 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011631 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011632#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011633 if (nch_flag || silent) {
11634 tcgetattr(0, &tty);
11635 old_tty = tty;
11636 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011637 tty.c_lflag &= ~ICANON;
11638 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011639 }
11640 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011641 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011642
11643 }
11644 tcsetattr(0, TCSANOW, &tty);
11645 }
11646#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011647#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011648 if (ts.tv_sec || ts.tv_usec) {
11649 FD_ZERO (&set);
11650 FD_SET (0, &set);
11651
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011652 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011653 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011654#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011655 if (nch_flag)
11656 tcsetattr(0, TCSANOW, &old_tty);
11657#endif
11658 return 1;
11659 }
11660 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011661#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011662 status = 0;
11663 startword = 1;
11664 backslash = 0;
11665 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011666#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011667 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011668#else
11669 for (;;)
11670#endif
11671 {
Eric Andersenc470f442003-07-28 09:56:35 +000011672 if (read(0, &c, 1) != 1) {
11673 status = 1;
11674 break;
11675 }
11676 if (c == '\0')
11677 continue;
11678 if (backslash) {
11679 backslash = 0;
11680 if (c != '\n')
11681 goto put;
11682 continue;
11683 }
11684 if (!rflag && c == '\\') {
11685 backslash++;
11686 continue;
11687 }
11688 if (c == '\n')
11689 break;
11690 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11691 continue;
11692 }
11693 startword = 0;
11694 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11695 STACKSTRNUL(p);
11696 setvar(*ap, stackblock(), 0);
11697 ap++;
11698 startword = 1;
11699 STARTSTACKSTR(p);
11700 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011701 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011702 STPUTC(c, p);
11703 }
11704 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011705#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011706 if (nch_flag || silent)
11707 tcsetattr(0, TCSANOW, &old_tty);
11708#endif
11709
Eric Andersenc470f442003-07-28 09:56:35 +000011710 STACKSTRNUL(p);
11711 /* Remove trailing blanks */
11712 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11713 *p = '\0';
11714 setvar(*ap, stackblock(), 0);
11715 while (*++ap != NULL)
11716 setvar(*ap, nullstr, 0);
11717 return status;
11718}
11719
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011720static int
11721umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011722{
11723 static const char permuser[3] = "ugo";
11724 static const char permmode[3] = "rwx";
11725 static const short int permmask[] = {
11726 S_IRUSR, S_IWUSR, S_IXUSR,
11727 S_IRGRP, S_IWGRP, S_IXGRP,
11728 S_IROTH, S_IWOTH, S_IXOTH
11729 };
11730
11731 char *ap;
11732 mode_t mask;
11733 int i;
11734 int symbolic_mode = 0;
11735
11736 while (nextopt("S") != '\0') {
11737 symbolic_mode = 1;
11738 }
11739
Denis Vlasenkob012b102007-02-19 22:43:01 +000011740 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011741 mask = umask(0);
11742 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011743 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011744
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011745 ap = *argptr;
11746 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011747 if (symbolic_mode) {
11748 char buf[18];
11749 char *p = buf;
11750
11751 for (i = 0; i < 3; i++) {
11752 int j;
11753
11754 *p++ = permuser[i];
11755 *p++ = '=';
11756 for (j = 0; j < 3; j++) {
11757 if ((mask & permmask[3 * i + j]) == 0) {
11758 *p++ = permmode[j];
11759 }
11760 }
11761 *p++ = ',';
11762 }
11763 *--p = 0;
11764 puts(buf);
11765 } else {
11766 out1fmt("%.4o\n", mask);
11767 }
11768 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011769 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011770 mask = 0;
11771 do {
11772 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011773 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011774 mask = (mask << 3) + (*ap - '0');
11775 } while (*++ap != '\0');
11776 umask(mask);
11777 } else {
11778 mask = ~mask & 0777;
11779 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011780 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011781 }
11782 umask(~mask & 0777);
11783 }
11784 }
11785 return 0;
11786}
11787
11788/*
11789 * ulimit builtin
11790 *
11791 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11792 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11793 * ash by J.T. Conklin.
11794 *
11795 * Public domain.
11796 */
11797
11798struct limits {
11799 const char *name;
11800 int cmd;
11801 int factor; /* multiply by to get rlim_{cur,max} values */
11802 char option;
11803};
11804
11805static const struct limits limits[] = {
11806#ifdef RLIMIT_CPU
11807 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11808#endif
11809#ifdef RLIMIT_FSIZE
11810 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11811#endif
11812#ifdef RLIMIT_DATA
11813 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11814#endif
11815#ifdef RLIMIT_STACK
11816 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11817#endif
11818#ifdef RLIMIT_CORE
11819 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11820#endif
11821#ifdef RLIMIT_RSS
11822 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11823#endif
11824#ifdef RLIMIT_MEMLOCK
11825 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11826#endif
11827#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011828 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011829#endif
11830#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011831 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011832#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011833#ifdef RLIMIT_AS
11834 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011835#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011836#ifdef RLIMIT_LOCKS
11837 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011838#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011839 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011840};
11841
Glenn L McGrath76620622004-01-13 10:19:37 +000011842enum limtype { SOFT = 0x1, HARD = 0x2 };
11843
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011844static void
11845printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011846 const struct limits *l)
11847{
11848 rlim_t val;
11849
11850 val = limit->rlim_max;
11851 if (how & SOFT)
11852 val = limit->rlim_cur;
11853
11854 if (val == RLIM_INFINITY)
11855 out1fmt("unlimited\n");
11856 else {
11857 val /= l->factor;
11858 out1fmt("%lld\n", (long long) val);
11859 }
11860}
11861
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011862static int
Eric Andersenc470f442003-07-28 09:56:35 +000011863ulimitcmd(int argc, char **argv)
11864{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011865 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011866 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011867 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011868 const struct limits *l;
11869 int set, all = 0;
11870 int optc, what;
11871 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011872
11873 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011874 while ((optc = nextopt("HSa"
11875#ifdef RLIMIT_CPU
11876 "t"
11877#endif
11878#ifdef RLIMIT_FSIZE
11879 "f"
11880#endif
11881#ifdef RLIMIT_DATA
11882 "d"
11883#endif
11884#ifdef RLIMIT_STACK
11885 "s"
11886#endif
11887#ifdef RLIMIT_CORE
11888 "c"
11889#endif
11890#ifdef RLIMIT_RSS
11891 "m"
11892#endif
11893#ifdef RLIMIT_MEMLOCK
11894 "l"
11895#endif
11896#ifdef RLIMIT_NPROC
11897 "p"
11898#endif
11899#ifdef RLIMIT_NOFILE
11900 "n"
11901#endif
11902#ifdef RLIMIT_AS
11903 "v"
11904#endif
11905#ifdef RLIMIT_LOCKS
11906 "w"
11907#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011908 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011909 switch (optc) {
11910 case 'H':
11911 how = HARD;
11912 break;
11913 case 'S':
11914 how = SOFT;
11915 break;
11916 case 'a':
11917 all = 1;
11918 break;
11919 default:
11920 what = optc;
11921 }
11922
Glenn L McGrath76620622004-01-13 10:19:37 +000011923 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011924 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011925
11926 set = *argptr ? 1 : 0;
11927 if (set) {
11928 char *p = *argptr;
11929
11930 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011931 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011932 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011933 val = RLIM_INFINITY;
11934 else {
11935 val = (rlim_t) 0;
11936
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011937 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011938 val = (val * 10) + (long)(c - '0');
11939 if (val < (rlim_t) 0)
11940 break;
11941 }
11942 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011943 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011944 val *= l->factor;
11945 }
11946 }
11947 if (all) {
11948 for (l = limits; l->name; l++) {
11949 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011950 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011951 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011952 }
11953 return 0;
11954 }
11955
11956 getrlimit(l->cmd, &limit);
11957 if (set) {
11958 if (how & HARD)
11959 limit.rlim_max = val;
11960 if (how & SOFT)
11961 limit.rlim_cur = val;
11962 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011963 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011964 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011965 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011966 }
11967 return 0;
11968}
11969
Eric Andersen90898442003-08-06 11:20:52 +000011970
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011971/* ============ Math support */
11972
Denis Vlasenko131ae172007-02-18 13:00:19 +000011973#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011974
11975/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11976
11977 Permission is hereby granted, free of charge, to any person obtaining
11978 a copy of this software and associated documentation files (the
11979 "Software"), to deal in the Software without restriction, including
11980 without limitation the rights to use, copy, modify, merge, publish,
11981 distribute, sublicense, and/or sell copies of the Software, and to
11982 permit persons to whom the Software is furnished to do so, subject to
11983 the following conditions:
11984
11985 The above copyright notice and this permission notice shall be
11986 included in all copies or substantial portions of the Software.
11987
11988 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11989 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11990 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11991 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11992 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11993 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11994 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11995*/
11996
11997/* This is my infix parser/evaluator. It is optimized for size, intended
11998 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011999 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012000 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012001 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012002 * be that which POSIX specifies for shells. */
12003
12004/* The code uses a simple two-stack algorithm. See
12005 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012006 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012007 * this is based (this code differs in that it applies operators immediately
12008 * to the stack instead of adding them to a queue to end up with an
12009 * expression). */
12010
12011/* To use the routine, call it with an expression string and error return
12012 * pointer */
12013
12014/*
12015 * Aug 24, 2001 Manuel Novoa III
12016 *
12017 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12018 *
12019 * 1) In arith_apply():
12020 * a) Cached values of *numptr and &(numptr[-1]).
12021 * b) Removed redundant test for zero denominator.
12022 *
12023 * 2) In arith():
12024 * a) Eliminated redundant code for processing operator tokens by moving
12025 * to a table-based implementation. Also folded handling of parens
12026 * into the table.
12027 * b) Combined all 3 loops which called arith_apply to reduce generated
12028 * code size at the cost of speed.
12029 *
12030 * 3) The following expressions were treated as valid by the original code:
12031 * 1() , 0! , 1 ( *3 ) .
12032 * These bugs have been fixed by internally enclosing the expression in
12033 * parens and then checking that all binary ops and right parens are
12034 * preceded by a valid expression (NUM_TOKEN).
12035 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012036 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012037 * ctype's isspace() if it is used by another busybox applet or if additional
12038 * whitespace chars should be considered. Look below the "#include"s for a
12039 * precompiler test.
12040 */
12041
12042/*
12043 * Aug 26, 2001 Manuel Novoa III
12044 *
12045 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12046 *
12047 * Merge in Aaron's comments previously posted to the busybox list,
12048 * modified slightly to take account of my changes to the code.
12049 *
12050 */
12051
12052/*
12053 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12054 *
12055 * - allow access to variable,
12056 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12057 * - realize assign syntax (VAR=expr, +=, *= etc)
12058 * - realize exponentiation (** operator)
12059 * - realize comma separated - expr, expr
12060 * - realise ++expr --expr expr++ expr--
12061 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012062 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012063 * - was restored loses XOR operator
12064 * - remove one goto label, added three ;-)
12065 * - protect $((num num)) as true zero expr (Manuel`s error)
12066 * - always use special isspace(), see comment from bash ;-)
12067 */
12068
Eric Andersen90898442003-08-06 11:20:52 +000012069#define arith_isspace(arithval) \
12070 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12071
Eric Andersen90898442003-08-06 11:20:52 +000012072typedef unsigned char operator;
12073
12074/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012075 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012076 * precedence. The ID portion is so that multiple operators can have the
12077 * same precedence, ensuring that the leftmost one is evaluated first.
12078 * Consider * and /. */
12079
12080#define tok_decl(prec,id) (((id)<<5)|(prec))
12081#define PREC(op) ((op) & 0x1F)
12082
12083#define TOK_LPAREN tok_decl(0,0)
12084
12085#define TOK_COMMA tok_decl(1,0)
12086
12087#define TOK_ASSIGN tok_decl(2,0)
12088#define TOK_AND_ASSIGN tok_decl(2,1)
12089#define TOK_OR_ASSIGN tok_decl(2,2)
12090#define TOK_XOR_ASSIGN tok_decl(2,3)
12091#define TOK_PLUS_ASSIGN tok_decl(2,4)
12092#define TOK_MINUS_ASSIGN tok_decl(2,5)
12093#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12094#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12095
12096#define TOK_MUL_ASSIGN tok_decl(3,0)
12097#define TOK_DIV_ASSIGN tok_decl(3,1)
12098#define TOK_REM_ASSIGN tok_decl(3,2)
12099
12100/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012101#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012102
12103/* conditional is right associativity too */
12104#define TOK_CONDITIONAL tok_decl(4,0)
12105#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12106
12107#define TOK_OR tok_decl(5,0)
12108
12109#define TOK_AND tok_decl(6,0)
12110
12111#define TOK_BOR tok_decl(7,0)
12112
12113#define TOK_BXOR tok_decl(8,0)
12114
12115#define TOK_BAND tok_decl(9,0)
12116
12117#define TOK_EQ tok_decl(10,0)
12118#define TOK_NE tok_decl(10,1)
12119
12120#define TOK_LT tok_decl(11,0)
12121#define TOK_GT tok_decl(11,1)
12122#define TOK_GE tok_decl(11,2)
12123#define TOK_LE tok_decl(11,3)
12124
12125#define TOK_LSHIFT tok_decl(12,0)
12126#define TOK_RSHIFT tok_decl(12,1)
12127
12128#define TOK_ADD tok_decl(13,0)
12129#define TOK_SUB tok_decl(13,1)
12130
12131#define TOK_MUL tok_decl(14,0)
12132#define TOK_DIV tok_decl(14,1)
12133#define TOK_REM tok_decl(14,2)
12134
12135/* exponent is right associativity */
12136#define TOK_EXPONENT tok_decl(15,1)
12137
12138/* For now unary operators. */
12139#define UNARYPREC 16
12140#define TOK_BNOT tok_decl(UNARYPREC,0)
12141#define TOK_NOT tok_decl(UNARYPREC,1)
12142
12143#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12144#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12145
12146#define PREC_PRE (UNARYPREC+2)
12147
12148#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12149#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12150
12151#define PREC_POST (UNARYPREC+3)
12152
12153#define TOK_POST_INC tok_decl(PREC_POST, 0)
12154#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12155
12156#define SPEC_PREC (UNARYPREC+4)
12157
12158#define TOK_NUM tok_decl(SPEC_PREC, 0)
12159#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12160
12161#define NUMPTR (*numstackptr)
12162
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012163static int
12164tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012165{
12166 operator prec = PREC(op);
12167
12168 convert_prec_is_assing(prec);
12169 return (prec == PREC(TOK_ASSIGN) ||
12170 prec == PREC_PRE || prec == PREC_POST);
12171}
12172
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012173static int
12174is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012175{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012176 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12177 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012178}
12179
Eric Andersen90898442003-08-06 11:20:52 +000012180typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012181 arith_t val;
12182 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012183 char contidional_second_val_initialized;
12184 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012185 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012186} v_n_t;
12187
Eric Andersen90898442003-08-06 11:20:52 +000012188typedef struct CHK_VAR_RECURSIVE_LOOPED {
12189 const char *var;
12190 struct CHK_VAR_RECURSIVE_LOOPED *next;
12191} chk_var_recursive_looped_t;
12192
12193static chk_var_recursive_looped_t *prev_chk_var_recursive;
12194
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012195static int
12196arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012197{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012198 if (t->var) {
12199 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012200
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012201 if (p) {
12202 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012203
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012204 /* recursive try as expression */
12205 chk_var_recursive_looped_t *cur;
12206 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012207
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012208 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12209 if (strcmp(cur->var, t->var) == 0) {
12210 /* expression recursion loop detected */
12211 return -5;
12212 }
12213 }
12214 /* save current lookuped var name */
12215 cur = prev_chk_var_recursive;
12216 cur_save.var = t->var;
12217 cur_save.next = cur;
12218 prev_chk_var_recursive = &cur_save;
12219
12220 t->val = arith (p, &errcode);
12221 /* restore previous ptr after recursiving */
12222 prev_chk_var_recursive = cur;
12223 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012224 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012225 /* allow undefined var as 0 */
12226 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012227 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012228 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012229}
12230
12231/* "applying" a token means performing it on the top elements on the integer
12232 * stack. For a unary operator it will only change the top element, but a
12233 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012234static int
12235arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012236{
Eric Andersen90898442003-08-06 11:20:52 +000012237 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012238 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012239 int ret_arith_lookup_val;
12240
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012241 /* There is no operator that can work without arguments */
12242 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012243 numptr_m1 = NUMPTR - 1;
12244
12245 /* check operand is var with noninteger value */
12246 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012247 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012248 return ret_arith_lookup_val;
12249
12250 rez = numptr_m1->val;
12251 if (op == TOK_UMINUS)
12252 rez *= -1;
12253 else if (op == TOK_NOT)
12254 rez = !rez;
12255 else if (op == TOK_BNOT)
12256 rez = ~rez;
12257 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12258 rez++;
12259 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12260 rez--;
12261 else if (op != TOK_UPLUS) {
12262 /* Binary operators */
12263
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012264 /* check and binary operators need two arguments */
12265 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012266
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012267 /* ... and they pop one */
12268 --NUMPTR;
12269 numptr_val = rez;
12270 if (op == TOK_CONDITIONAL) {
12271 if (! numptr_m1->contidional_second_val_initialized) {
12272 /* protect $((expr1 ? expr2)) without ": expr" */
12273 goto err;
12274 }
12275 rez = numptr_m1->contidional_second_val;
12276 } else if (numptr_m1->contidional_second_val_initialized) {
12277 /* protect $((expr1 : expr2)) without "expr ? " */
12278 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012279 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012280 numptr_m1 = NUMPTR - 1;
12281 if (op != TOK_ASSIGN) {
12282 /* check operand is var with noninteger value for not '=' */
12283 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12284 if (ret_arith_lookup_val)
12285 return ret_arith_lookup_val;
12286 }
12287 if (op == TOK_CONDITIONAL) {
12288 numptr_m1->contidional_second_val = rez;
12289 }
12290 rez = numptr_m1->val;
12291 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012292 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012293 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012294 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012295 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012296 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012297 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012298 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012299 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012300 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012301 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012302 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012303 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012304 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012305 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012306 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012307 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012308 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012309 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012310 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012311 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012312 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012313 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012314 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012315 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012316 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012317 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012318 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012319 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012320 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012321 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012322 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012323 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012324 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012325 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012326 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012327 /* protect $((expr : expr)) without "expr ? " */
12328 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012329 }
12330 numptr_m1->contidional_second_val_initialized = op;
12331 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012332 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012333 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012334 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012335 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012336 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012337 return -3; /* exponent less than 0 */
12338 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012339 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012340
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012341 if (numptr_val)
12342 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012343 c *= rez;
12344 rez = c;
12345 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012346 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012347 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012348 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012349 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012350 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012351 rez %= numptr_val;
12352 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012353 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012354 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012355
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012356 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012357 /* Hmm, 1=2 ? */
12358 goto err;
12359 }
12360 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012361#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012362 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012363#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012364 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012365#endif
Eric Andersen90898442003-08-06 11:20:52 +000012366 setvar(numptr_m1->var, buf, 0);
12367 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012368 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012369 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012370 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012371 rez++;
12372 }
12373 numptr_m1->val = rez;
12374 /* protect geting var value, is number now */
12375 numptr_m1->var = NULL;
12376 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012377 err:
12378 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012379}
12380
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012381/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012382static const char op_tokens[] = {
12383 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12384 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12385 '<','<', 0, TOK_LSHIFT,
12386 '>','>', 0, TOK_RSHIFT,
12387 '|','|', 0, TOK_OR,
12388 '&','&', 0, TOK_AND,
12389 '!','=', 0, TOK_NE,
12390 '<','=', 0, TOK_LE,
12391 '>','=', 0, TOK_GE,
12392 '=','=', 0, TOK_EQ,
12393 '|','=', 0, TOK_OR_ASSIGN,
12394 '&','=', 0, TOK_AND_ASSIGN,
12395 '*','=', 0, TOK_MUL_ASSIGN,
12396 '/','=', 0, TOK_DIV_ASSIGN,
12397 '%','=', 0, TOK_REM_ASSIGN,
12398 '+','=', 0, TOK_PLUS_ASSIGN,
12399 '-','=', 0, TOK_MINUS_ASSIGN,
12400 '-','-', 0, TOK_POST_DEC,
12401 '^','=', 0, TOK_XOR_ASSIGN,
12402 '+','+', 0, TOK_POST_INC,
12403 '*','*', 0, TOK_EXPONENT,
12404 '!', 0, TOK_NOT,
12405 '<', 0, TOK_LT,
12406 '>', 0, TOK_GT,
12407 '=', 0, TOK_ASSIGN,
12408 '|', 0, TOK_BOR,
12409 '&', 0, TOK_BAND,
12410 '*', 0, TOK_MUL,
12411 '/', 0, TOK_DIV,
12412 '%', 0, TOK_REM,
12413 '+', 0, TOK_ADD,
12414 '-', 0, TOK_SUB,
12415 '^', 0, TOK_BXOR,
12416 /* uniq */
12417 '~', 0, TOK_BNOT,
12418 ',', 0, TOK_COMMA,
12419 '?', 0, TOK_CONDITIONAL,
12420 ':', 0, TOK_CONDITIONAL_SEP,
12421 ')', 0, TOK_RPAREN,
12422 '(', 0, TOK_LPAREN,
12423 0
12424};
12425/* ptr to ")" */
12426#define endexpression &op_tokens[sizeof(op_tokens)-7]
12427
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012428static arith_t
12429arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012430{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012431 char arithval; /* Current character under analysis */
12432 operator lasttok, op;
12433 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012434
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012435 const char *p = endexpression;
12436 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012437
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012438 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012439
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012440 /* Stack of integers */
12441 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12442 * in any given correct or incorrect expression is left as an exercise to
12443 * the reader. */
12444 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12445 *numstackptr = numstack;
12446 /* Stack of operator tokens */
12447 operator *stack = alloca((datasizes) * sizeof(operator)),
12448 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012449
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012450 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12451 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012452
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012453 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012454 arithval = *expr;
12455 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012456 if (p == endexpression) {
12457 /* Null expression. */
12458 return 0;
12459 }
12460
12461 /* This is only reached after all tokens have been extracted from the
12462 * input stream. If there are still tokens on the operator stack, they
12463 * are to be applied in order. At the end, there should be a final
12464 * result on the integer stack */
12465
12466 if (expr != endexpression + 1) {
12467 /* If we haven't done so already, */
12468 /* append a closing right paren */
12469 expr = endexpression;
12470 /* and let the loop process it. */
12471 continue;
12472 }
12473 /* At this point, we're done with the expression. */
12474 if (numstackptr != numstack+1) {
12475 /* ... but if there isn't, it's bad */
12476 err:
12477 return (*perrcode = -1);
12478 }
12479 if (numstack->var) {
12480 /* expression is $((var)) only, lookup now */
12481 errcode = arith_lookup_val(numstack);
12482 }
12483 ret:
12484 *perrcode = errcode;
12485 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012486 }
12487
Eric Andersen90898442003-08-06 11:20:52 +000012488 /* Continue processing the expression. */
12489 if (arith_isspace(arithval)) {
12490 /* Skip whitespace */
12491 goto prologue;
12492 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012493 p = endofname(expr);
12494 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012495 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012496
12497 numstackptr->var = alloca(var_name_size);
12498 safe_strncpy(numstackptr->var, expr, var_name_size);
12499 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012500 num:
Eric Andersen90898442003-08-06 11:20:52 +000012501 numstackptr->contidional_second_val_initialized = 0;
12502 numstackptr++;
12503 lasttok = TOK_NUM;
12504 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012505 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012506 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012507 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012508#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012509 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012510#else
12511 numstackptr->val = strtol(expr, (char **) &expr, 0);
12512#endif
Eric Andersen90898442003-08-06 11:20:52 +000012513 goto num;
12514 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012515 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012516 const char *o;
12517
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012518 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012519 /* strange operator not found */
12520 goto err;
12521 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012522 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012523 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012524 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012525 /* found */
12526 expr = o - 1;
12527 break;
12528 }
12529 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012530 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012531 p++;
12532 /* skip zero delim */
12533 p++;
12534 }
12535 op = p[1];
12536
12537 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012538 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12539 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012540
12541 /* Plus and minus are binary (not unary) _only_ if the last
12542 * token was as number, or a right paren (which pretends to be
12543 * a number, since it evaluates to one). Think about it.
12544 * It makes sense. */
12545 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012546 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012547 case TOK_ADD:
12548 op = TOK_UPLUS;
12549 break;
12550 case TOK_SUB:
12551 op = TOK_UMINUS;
12552 break;
12553 case TOK_POST_INC:
12554 op = TOK_PRE_INC;
12555 break;
12556 case TOK_POST_DEC:
12557 op = TOK_PRE_DEC;
12558 break;
Eric Andersen90898442003-08-06 11:20:52 +000012559 }
12560 }
12561 /* We don't want a unary operator to cause recursive descent on the
12562 * stack, because there can be many in a row and it could cause an
12563 * operator to be evaluated before its argument is pushed onto the
12564 * integer stack. */
12565 /* But for binary operators, "apply" everything on the operator
12566 * stack until we find an operator with a lesser priority than the
12567 * one we have just extracted. */
12568 /* Left paren is given the lowest priority so it will never be
12569 * "applied" in this way.
12570 * if associativity is right and priority eq, applied also skip
12571 */
12572 prec = PREC(op);
12573 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12574 /* not left paren or unary */
12575 if (lasttok != TOK_NUM) {
12576 /* binary op must be preceded by a num */
12577 goto err;
12578 }
12579 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012580 if (op == TOK_RPAREN) {
12581 /* The algorithm employed here is simple: while we don't
12582 * hit an open paren nor the bottom of the stack, pop
12583 * tokens and apply them */
12584 if (stackptr[-1] == TOK_LPAREN) {
12585 --stackptr;
12586 /* Any operator directly after a */
12587 lasttok = TOK_NUM;
12588 /* close paren should consider itself binary */
12589 goto prologue;
12590 }
12591 } else {
12592 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012593
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012594 convert_prec_is_assing(prec);
12595 convert_prec_is_assing(prev_prec);
12596 if (prev_prec < prec)
12597 break;
12598 /* check right assoc */
12599 if (prev_prec == prec && is_right_associativity(prec))
12600 break;
12601 }
12602 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12603 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012604 }
12605 if (op == TOK_RPAREN) {
12606 goto err;
12607 }
12608 }
12609
12610 /* Push this operator to the stack and remember it. */
12611 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012612 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012613 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012614 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012615}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012616#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012617
12618
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012619/* ============ main() and helpers */
12620
12621/*
12622 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012623 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012624static void exitshell(void) ATTRIBUTE_NORETURN;
12625static void
12626exitshell(void)
12627{
12628 struct jmploc loc;
12629 char *p;
12630 int status;
12631
12632 status = exitstatus;
12633 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12634 if (setjmp(loc.loc)) {
12635 if (exception == EXEXIT)
12636/* dash bug: it just does _exit(exitstatus) here
12637 * but we have to do setjobctl(0) first!
12638 * (bug is still not fixed in dash-0.5.3 - if you run dash
12639 * under Midnight Commander, on exit from dash MC is backgrounded) */
12640 status = exitstatus;
12641 goto out;
12642 }
12643 exception_handler = &loc;
12644 p = trap[0];
12645 if (p) {
12646 trap[0] = NULL;
12647 evalstring(p, 0);
12648 }
12649 flush_stdout_stderr();
12650 out:
12651 setjobctl(0);
12652 _exit(status);
12653 /* NOTREACHED */
12654}
12655
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012656static void
12657init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012658{
12659 /* from input.c: */
12660 basepf.nextc = basepf.buf = basebuf;
12661
12662 /* from trap.c: */
12663 signal(SIGCHLD, SIG_DFL);
12664
12665 /* from var.c: */
12666 {
12667 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012668 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012669 const char *p;
12670 struct stat st1, st2;
12671
12672 initvar();
12673 for (envp = environ; envp && *envp; envp++) {
12674 if (strchr(*envp, '=')) {
12675 setvareq(*envp, VEXPORT|VTEXTFIXED);
12676 }
12677 }
12678
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012679 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012680 setvar("PPID", ppid, 0);
12681
12682 p = lookupvar("PWD");
12683 if (p)
12684 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12685 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12686 p = '\0';
12687 setpwd(p, 0);
12688 }
12689}
12690
12691/*
12692 * Process the shell command line arguments.
12693 */
12694static void
12695procargs(int argc, char **argv)
12696{
12697 int i;
12698 const char *xminusc;
12699 char **xargv;
12700
12701 xargv = argv;
12702 arg0 = xargv[0];
12703 if (argc > 0)
12704 xargv++;
12705 for (i = 0; i < NOPTS; i++)
12706 optlist[i] = 2;
12707 argptr = xargv;
12708 options(1);
12709 xargv = argptr;
12710 xminusc = minusc;
12711 if (*xargv == NULL) {
12712 if (xminusc)
12713 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12714 sflag = 1;
12715 }
12716 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12717 iflag = 1;
12718 if (mflag == 2)
12719 mflag = iflag;
12720 for (i = 0; i < NOPTS; i++)
12721 if (optlist[i] == 2)
12722 optlist[i] = 0;
12723#if DEBUG == 2
12724 debug = 1;
12725#endif
12726 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12727 if (xminusc) {
12728 minusc = *xargv++;
12729 if (*xargv)
12730 goto setarg0;
12731 } else if (!sflag) {
12732 setinputfile(*xargv, 0);
12733 setarg0:
12734 arg0 = *xargv++;
12735 commandname = arg0;
12736 }
12737
12738 shellparam.p = xargv;
12739#if ENABLE_ASH_GETOPTS
12740 shellparam.optind = 1;
12741 shellparam.optoff = -1;
12742#endif
12743 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12744 while (*xargv) {
12745 shellparam.nparam++;
12746 xargv++;
12747 }
12748 optschanged();
12749}
12750
12751/*
12752 * Read /etc/profile or .profile.
12753 */
12754static void
12755read_profile(const char *name)
12756{
12757 int skip;
12758
12759 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12760 return;
12761 skip = cmdloop(0);
12762 popfile();
12763 if (skip)
12764 exitshell();
12765}
12766
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012767/*
12768 * This routine is called when an error or an interrupt occurs in an
12769 * interactive shell and control is returned to the main command loop.
12770 */
12771static void
12772reset(void)
12773{
12774 /* from eval.c: */
12775 evalskip = 0;
12776 loopnest = 0;
12777 /* from input.c: */
12778 parselleft = parsenleft = 0; /* clear input buffer */
12779 popallfiles();
12780 /* from parser.c: */
12781 tokpushback = 0;
12782 checkkwd = 0;
12783 /* from redir.c: */
12784 clearredir(0);
12785}
12786
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012787#if PROFILE
12788static short profile_buf[16384];
12789extern int etext();
12790#endif
12791
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012792/*
12793 * Main routine. We initialize things, parse the arguments, execute
12794 * profiles if we're a login shell, and then call cmdloop to execute
12795 * commands. The setjmp call sets up the location to jump to when an
12796 * exception occurs. When an exception occurs the variable "state"
12797 * is used to figure out how far we had gotten.
12798 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012799int ash_main(int argc, char **argv);
12800int ash_main(int argc, char **argv)
12801{
12802 char *shinit;
12803 volatile int state;
12804 struct jmploc jmploc;
12805 struct stackmark smark;
12806
12807#ifdef __GLIBC__
12808 dash_errno = __errno_location();
12809#endif
12810
12811#if PROFILE
12812 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12813#endif
12814
12815#if ENABLE_FEATURE_EDITING
12816 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12817#endif
12818 state = 0;
12819 if (setjmp(jmploc.loc)) {
12820 int e;
12821 int s;
12822
12823 reset();
12824
12825 e = exception;
12826 if (e == EXERROR)
12827 exitstatus = 2;
12828 s = state;
12829 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12830 exitshell();
12831
12832 if (e == EXINT) {
12833 outcslow('\n', stderr);
12834 }
12835 popstackmark(&smark);
12836 FORCE_INT_ON; /* enable interrupts */
12837 if (s == 1)
12838 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012839 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012840 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012841 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012842 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012843 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012844 }
12845 exception_handler = &jmploc;
12846#if DEBUG
12847 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012848 trace_puts("Shell args: ");
12849 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012850#endif
12851 rootpid = getpid();
12852
12853#if ENABLE_ASH_RANDOM_SUPPORT
12854 rseed = rootpid + time(NULL);
12855#endif
12856 init();
12857 setstackmark(&smark);
12858 procargs(argc, argv);
12859#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12860 if (iflag) {
12861 const char *hp = lookupvar("HISTFILE");
12862
12863 if (hp == NULL) {
12864 hp = lookupvar("HOME");
12865 if (hp != NULL) {
12866 char *defhp = concat_path_file(hp, ".ash_history");
12867 setvar("HISTFILE", defhp, 0);
12868 free(defhp);
12869 }
12870 }
12871 }
12872#endif
12873 if (argv[0] && argv[0][0] == '-')
12874 isloginsh = 1;
12875 if (isloginsh) {
12876 state = 1;
12877 read_profile("/etc/profile");
12878 state1:
12879 state = 2;
12880 read_profile(".profile");
12881 }
12882 state2:
12883 state = 3;
12884 if (
12885#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012886 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012887#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012888 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012889 ) {
12890 shinit = lookupvar("ENV");
12891 if (shinit != NULL && *shinit != '\0') {
12892 read_profile(shinit);
12893 }
12894 }
12895 state3:
12896 state = 4;
12897 if (minusc)
12898 evalstring(minusc, 0);
12899
12900 if (sflag || minusc == NULL) {
12901#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12902 if ( iflag ) {
12903 const char *hp = lookupvar("HISTFILE");
12904
12905 if (hp != NULL)
12906 line_input_state->hist_file = hp;
12907 }
12908#endif
12909 state4: /* XXX ??? - why isn't this before the "if" statement */
12910 cmdloop(1);
12911 }
12912#if PROFILE
12913 monitor(0);
12914#endif
12915#ifdef GPROF
12916 {
12917 extern void _mcleanup(void);
12918 _mcleanup();
12919 }
12920#endif
12921 exitshell();
12922 /* NOTREACHED */
12923}
12924
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012925#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012926const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012927int main(int argc, char **argv)
12928{
12929 return ash_main(argc, argv);
12930}
12931#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012932
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012933
Eric Andersendf82f612001-06-28 07:46:40 +000012934/*-
12935 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012936 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012937 *
12938 * This code is derived from software contributed to Berkeley by
12939 * Kenneth Almquist.
12940 *
12941 * Redistribution and use in source and binary forms, with or without
12942 * modification, are permitted provided that the following conditions
12943 * are met:
12944 * 1. Redistributions of source code must retain the above copyright
12945 * notice, this list of conditions and the following disclaimer.
12946 * 2. Redistributions in binary form must reproduce the above copyright
12947 * notice, this list of conditions and the following disclaimer in the
12948 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012949 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012950 * may be used to endorse or promote products derived from this software
12951 * without specific prior written permission.
12952 *
12953 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12954 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12955 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12956 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12957 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12958 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12959 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12960 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12961 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12962 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12963 * SUCH DAMAGE.
12964 */