blob: b1d22e5d2fc2562ab3e3cd1c4363afec8de06c71 [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
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000056#include "busybox.h" /* for struct bb_applet */
Denis Vlasenkob012b102007-02-19 22:43:01 +000057#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
Denis Vlasenko5b340832007-05-17 23:02:14 +000063extern char **environ;
Eric Andersencb57d552001-06-28 07:25:16 +000064
Denis Vlasenkob012b102007-02-19 22:43:01 +000065#if defined(__uClinux__)
66#error "Do not even bother, ash will not run on uClinux"
67#endif
68
Denis Vlasenkob012b102007-02-19 22:43:01 +000069
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000070/* ============ Misc helpers */
71
72#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
73
74/* C99 say: "char" declaration may be signed or unsigned default */
75#define signed_char2int(sc) ((int)((signed char)sc))
76
77
Denis Vlasenkob012b102007-02-19 22:43:01 +000078/* ============ Shell options */
79
80static const char *const optletters_optnames[] = {
81 "e" "errexit",
82 "f" "noglob",
83 "I" "ignoreeof",
84 "i" "interactive",
85 "m" "monitor",
86 "n" "noexec",
87 "s" "stdin",
88 "x" "xtrace",
89 "v" "verbose",
90 "C" "noclobber",
91 "a" "allexport",
92 "b" "notify",
93 "u" "nounset",
94 "\0" "vi",
95#if DEBUG
96 "\0" "nolog",
97 "\0" "debug",
98#endif
99};
100
101#define optletters(n) optletters_optnames[(n)][0]
102#define optnames(n) (&optletters_optnames[(n)][1])
103
104#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
105
106static char optlist[NOPTS];
107
108#define eflag optlist[0]
109#define fflag optlist[1]
110#define Iflag optlist[2]
111#define iflag optlist[3]
112#define mflag optlist[4]
113#define nflag optlist[5]
114#define sflag optlist[6]
115#define xflag optlist[7]
116#define vflag optlist[8]
117#define Cflag optlist[9]
118#define aflag optlist[10]
119#define bflag optlist[11]
120#define uflag optlist[12]
121#define viflag optlist[13]
122#if DEBUG
123#define nolog optlist[14]
124#define debug optlist[15]
125#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000126
127
Denis Vlasenkob012b102007-02-19 22:43:01 +0000128/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000129
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000130#ifdef __GLIBC__
131/* glibc sucks */
132static int *dash_errno;
133#undef errno
134#define errno (*dash_errno)
135#endif
136
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000137static char nullstr[1]; /* zero length string */
138static const char homestr[] = "HOME";
139static const char snlfmt[] = "%s\n";
140static const char illnum[] = "Illegal number: %s";
141
Denis Vlasenkocc571512007-02-23 21:10:35 +0000142static char *minusc; /* argument to -c option */
143
Denis Vlasenkoa624c112007-02-19 22:45:43 +0000144static int isloginsh;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000145/* pid of main shell */
146static int rootpid;
147/* shell level: 0 for the main shell, 1 for its children, and so on */
148static int shlvl;
149#define rootshell (!shlvl)
150/* trap handler commands */
151static char *trap[NSIG];
152/* current value of signal */
153static char sigmode[NSIG - 1];
154/* indicates specified signal received */
155static char gotsig[NSIG - 1];
156static char *arg0; /* value of $0 */
Eric Andersenc470f442003-07-28 09:56:35 +0000157
158
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000159/* ============ Interrupts / exceptions */
160
161/*
Eric Andersenc470f442003-07-28 09:56:35 +0000162 * We enclose jmp_buf in a structure so that we can declare pointers to
163 * jump locations. The global variable handler contains the location to
164 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000165 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000166 * exception handlers, the user should save the value of handler on entry
167 * to an inner scope, set handler to point to a jmploc structure for the
168 * inner scope, and restore handler on exit from the scope.
169 */
Eric Andersenc470f442003-07-28 09:56:35 +0000170struct jmploc {
171 jmp_buf loc;
172};
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000173static struct jmploc *exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +0000174static int exception;
Eric Andersenc470f442003-07-28 09:56:35 +0000175/* exceptions */
176#define EXINT 0 /* SIGINT received */
177#define EXERROR 1 /* a generic error */
178#define EXSHELLPROC 2 /* execute a shell procedure */
179#define EXEXEC 3 /* command execution failed */
180#define EXEXIT 4 /* exit the shell */
181#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000182static volatile int suppressint;
183static volatile sig_atomic_t intpending;
Eric Andersenc470f442003-07-28 09:56:35 +0000184/* do we generate EXSIG events */
185static int exsig;
186/* last pending signal */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000187static volatile sig_atomic_t pendingsig;
Eric Andersen2870d962001-07-02 17:27:21 +0000188
189/*
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000190 * Sigmode records the current value of the signal handlers for the various
191 * modes. A value of zero means that the current handler is not known.
192 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
193 */
194
195#define S_DFL 1 /* default signal handling (SIG_DFL) */
196#define S_CATCH 2 /* signal is caught */
197#define S_IGN 3 /* signal is ignored (SIG_IGN) */
198#define S_HARD_IGN 4 /* signal is ignored permenantly */
199#define S_RESET 5 /* temporary - to reset a hard ignored sig */
200
201/*
Eric Andersen2870d962001-07-02 17:27:21 +0000202 * These macros allow the user to suspend the handling of interrupt signals
203 * over a period of time. This is similar to SIGHOLD to or sigblock, but
204 * much more efficient and portable. (But hacking the kernel is so much
205 * more fun than worrying about efficiency and portability. :-))
206 */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000207#define INT_OFF \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000208 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000209 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000210 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000211 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000212
213/*
214 * Called to raise an exception. Since C doesn't include exceptions, we
215 * just do a longjmp to the exception handler. The type of exception is
216 * stored in the global variable "exception".
217 */
218static void raise_exception(int) ATTRIBUTE_NORETURN;
219static void
220raise_exception(int e)
221{
222#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000223 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000224 abort();
225#endif
226 INT_OFF;
227 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000228 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000229}
230
231/*
232 * Called from trap.c when a SIGINT is received. (If the user specifies
233 * that SIGINT is to be trapped or ignored using the trap builtin, then
234 * this routine is not called.) Suppressint is nonzero when interrupts
235 * are held using the INT_OFF macro. (The test for iflag is just
236 * defensive programming.)
237 */
238static void raise_interrupt(void) ATTRIBUTE_NORETURN;
239static void
240raise_interrupt(void)
241{
242 int i;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000243 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000244
245 intpending = 0;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000246 /* Signal is not automatically re-enabled after it is raised,
247 * do it ourself */
248 sigemptyset(&mask);
249 sigprocmask(SIG_SETMASK, &mask, 0);
250 /* pendingsig = 0; - now done in onsig() */
251
Denis Vlasenkob012b102007-02-19 22:43:01 +0000252 i = EXSIG;
253 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
254 if (!(rootshell && iflag)) {
255 signal(SIGINT, SIG_DFL);
256 raise(SIGINT);
257 }
258 i = EXINT;
259 }
260 raise_exception(i);
261 /* NOTREACHED */
262}
263
264#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000265static void
266int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000267{
268 if (--suppressint == 0 && intpending) {
269 raise_interrupt();
270 }
271}
272#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000273static void
274force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000275{
276 suppressint = 0;
277 if (intpending)
278 raise_interrupt();
279}
280#define FORCE_INT_ON force_int_on()
281#else
282#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000283 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000284 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000285 if (--suppressint == 0 && intpending) \
286 raise_interrupt(); \
287 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000288#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000289 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000290 xbarrier(); \
291 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000292 if (intpending) \
293 raise_interrupt(); \
294 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000295#endif /* ASH_OPTIMIZE_FOR_SIZE */
296
297#define SAVE_INT(v) ((v) = suppressint)
298
299#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000300 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000301 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000302 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000303 if (suppressint == 0 && intpending) \
304 raise_interrupt(); \
305 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000306
307#define EXSIGON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000308 do { \
Eric Andersenc470f442003-07-28 09:56:35 +0000309 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000310 xbarrier(); \
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000311 if (pendingsig) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000312 raise_exception(EXSIG); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000313 } while (0)
Eric Andersenc470f442003-07-28 09:56:35 +0000314/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000315
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000316/*
317 * Ignore a signal. Only one usage site - in forkchild()
318 */
319static void
320ignoresig(int signo)
321{
322 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
323 signal(signo, SIG_IGN);
324 }
325 sigmode[signo - 1] = S_HARD_IGN;
326}
327
328/*
329 * Signal handler. Only one usage site - in setsignal()
330 */
331static void
332onsig(int signo)
333{
334 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000335 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000336
337 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000338 if (!suppressint) {
339 pendingsig = 0;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000340 raise_interrupt();
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000341 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000342 intpending = 1;
343 }
344}
345
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000346
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000347/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000348
Eric Andersenc470f442003-07-28 09:56:35 +0000349static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000350outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000351{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000352 INT_OFF;
353 fputs(p, file);
354 INT_ON;
355}
356
357static void
358flush_stdout_stderr(void)
359{
360 INT_OFF;
361 fflush(stdout);
362 fflush(stderr);
363 INT_ON;
364}
365
366static void
367flush_stderr(void)
368{
369 INT_OFF;
370 fflush(stderr);
371 INT_ON;
372}
373
374static void
375outcslow(int c, FILE *dest)
376{
377 INT_OFF;
378 putc(c, dest);
379 fflush(dest);
380 INT_ON;
381}
382
383static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
384static int
385out1fmt(const char *fmt, ...)
386{
387 va_list ap;
388 int r;
389
390 INT_OFF;
391 va_start(ap, fmt);
392 r = vprintf(fmt, ap);
393 va_end(ap);
394 INT_ON;
395 return r;
396}
397
398static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
399static int
400fmtstr(char *outbuf, size_t length, const char *fmt, ...)
401{
402 va_list ap;
403 int ret;
404
405 va_start(ap, fmt);
406 INT_OFF;
407 ret = vsnprintf(outbuf, length, fmt, ap);
408 va_end(ap);
409 INT_ON;
410 return ret;
411}
412
413static void
414out1str(const char *p)
415{
416 outstr(p, stdout);
417}
418
419static void
420out2str(const char *p)
421{
422 outstr(p, stderr);
423 flush_stderr();
424}
425
426
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000427/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000428
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000429/* control characters in argument strings */
430#define CTLESC '\201' /* escape next character */
431#define CTLVAR '\202' /* variable defn */
432#define CTLENDVAR '\203'
433#define CTLBACKQ '\204'
434#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
435/* CTLBACKQ | CTLQUOTE == '\205' */
436#define CTLARI '\206' /* arithmetic expression */
437#define CTLENDARI '\207'
438#define CTLQUOTEMARK '\210'
439
440/* variable substitution byte (follows CTLVAR) */
441#define VSTYPE 0x0f /* type of variable substitution */
442#define VSNUL 0x10 /* colon--treat the empty string as unset */
443#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
444
445/* values of VSTYPE field */
446#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
447#define VSMINUS 0x2 /* ${var-text} */
448#define VSPLUS 0x3 /* ${var+text} */
449#define VSQUESTION 0x4 /* ${var?message} */
450#define VSASSIGN 0x5 /* ${var=text} */
451#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
452#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
453#define VSTRIMLEFT 0x8 /* ${var#pattern} */
454#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
455#define VSLENGTH 0xa /* ${#var} */
456
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000457static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
458
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000459#define NCMD 0
460#define NPIPE 1
461#define NREDIR 2
462#define NBACKGND 3
463#define NSUBSHELL 4
464#define NAND 5
465#define NOR 6
466#define NSEMI 7
467#define NIF 8
468#define NWHILE 9
469#define NUNTIL 10
470#define NFOR 11
471#define NCASE 12
472#define NCLIST 13
473#define NDEFUN 14
474#define NARG 15
475#define NTO 16
476#define NCLOBBER 17
477#define NFROM 18
478#define NFROMTO 19
479#define NAPPEND 20
480#define NTOFD 21
481#define NFROMFD 22
482#define NHERE 23
483#define NXHERE 24
484#define NNOT 25
485
486union node;
487
488struct ncmd {
489 int type;
490 union node *assign;
491 union node *args;
492 union node *redirect;
493};
494
495struct npipe {
496 int type;
497 int backgnd;
498 struct nodelist *cmdlist;
499};
500
501struct nredir {
502 int type;
503 union node *n;
504 union node *redirect;
505};
506
507struct nbinary {
508 int type;
509 union node *ch1;
510 union node *ch2;
511};
512
513struct nif {
514 int type;
515 union node *test;
516 union node *ifpart;
517 union node *elsepart;
518};
519
520struct nfor {
521 int type;
522 union node *args;
523 union node *body;
524 char *var;
525};
526
527struct ncase {
528 int type;
529 union node *expr;
530 union node *cases;
531};
532
533struct nclist {
534 int type;
535 union node *next;
536 union node *pattern;
537 union node *body;
538};
539
540struct narg {
541 int type;
542 union node *next;
543 char *text;
544 struct nodelist *backquote;
545};
546
547struct nfile {
548 int type;
549 union node *next;
550 int fd;
551 union node *fname;
552 char *expfname;
553};
554
555struct ndup {
556 int type;
557 union node *next;
558 int fd;
559 int dupfd;
560 union node *vname;
561};
562
563struct nhere {
564 int type;
565 union node *next;
566 int fd;
567 union node *doc;
568};
569
570struct nnot {
571 int type;
572 union node *com;
573};
574
575union node {
576 int type;
577 struct ncmd ncmd;
578 struct npipe npipe;
579 struct nredir nredir;
580 struct nbinary nbinary;
581 struct nif nif;
582 struct nfor nfor;
583 struct ncase ncase;
584 struct nclist nclist;
585 struct narg narg;
586 struct nfile nfile;
587 struct ndup ndup;
588 struct nhere nhere;
589 struct nnot nnot;
590};
591
592struct nodelist {
593 struct nodelist *next;
594 union node *n;
595};
596
597struct funcnode {
598 int count;
599 union node n;
600};
601
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000602/*
603 * Free a parse tree.
604 */
605static void
606freefunc(struct funcnode *f)
607{
608 if (f && --f->count < 0)
609 free(f);
610}
611
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000612
613/* ============ Debugging output */
614
615#if DEBUG
616
617static FILE *tracefile;
618
619static void
620trace_printf(const char *fmt, ...)
621{
622 va_list va;
623
624 if (debug != 1)
625 return;
626 va_start(va, fmt);
627 vfprintf(tracefile, fmt, va);
628 va_end(va);
629}
630
631static void
632trace_vprintf(const char *fmt, va_list va)
633{
634 if (debug != 1)
635 return;
636 vfprintf(tracefile, fmt, va);
637}
638
639static void
640trace_puts(const char *s)
641{
642 if (debug != 1)
643 return;
644 fputs(s, tracefile);
645}
646
647static void
648trace_puts_quoted(char *s)
649{
650 char *p;
651 char c;
652
653 if (debug != 1)
654 return;
655 putc('"', tracefile);
656 for (p = s; *p; p++) {
657 switch (*p) {
658 case '\n': c = 'n'; goto backslash;
659 case '\t': c = 't'; goto backslash;
660 case '\r': c = 'r'; goto backslash;
661 case '"': c = '"'; goto backslash;
662 case '\\': c = '\\'; goto backslash;
663 case CTLESC: c = 'e'; goto backslash;
664 case CTLVAR: c = 'v'; goto backslash;
665 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
666 case CTLBACKQ: c = 'q'; goto backslash;
667 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
668 backslash:
669 putc('\\', tracefile);
670 putc(c, tracefile);
671 break;
672 default:
673 if (*p >= ' ' && *p <= '~')
674 putc(*p, tracefile);
675 else {
676 putc('\\', tracefile);
677 putc(*p >> 6 & 03, tracefile);
678 putc(*p >> 3 & 07, tracefile);
679 putc(*p & 07, tracefile);
680 }
681 break;
682 }
683 }
684 putc('"', tracefile);
685}
686
687static void
688trace_puts_args(char **ap)
689{
690 if (debug != 1)
691 return;
692 if (!*ap)
693 return;
694 while (1) {
695 trace_puts_quoted(*ap);
696 if (!*++ap) {
697 putc('\n', tracefile);
698 break;
699 }
700 putc(' ', tracefile);
701 }
702}
703
704static void
705opentrace(void)
706{
707 char s[100];
708#ifdef O_APPEND
709 int flags;
710#endif
711
712 if (debug != 1) {
713 if (tracefile)
714 fflush(tracefile);
715 /* leave open because libedit might be using it */
716 return;
717 }
718 strcpy(s, "./trace");
719 if (tracefile) {
720 if (!freopen(s, "a", tracefile)) {
721 fprintf(stderr, "Can't re-open %s\n", s);
722 debug = 0;
723 return;
724 }
725 } else {
726 tracefile = fopen(s, "a");
727 if (tracefile == NULL) {
728 fprintf(stderr, "Can't open %s\n", s);
729 debug = 0;
730 return;
731 }
732 }
733#ifdef O_APPEND
734 flags = fcntl(fileno(tracefile), F_GETFL, 0);
735 if (flags >= 0)
736 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
737#endif
738 setlinebuf(tracefile);
739 fputs("\nTracing started.\n", tracefile);
740}
741
742static void
743indent(int amount, char *pfx, FILE *fp)
744{
745 int i;
746
747 for (i = 0; i < amount; i++) {
748 if (pfx && i == amount - 1)
749 fputs(pfx, fp);
750 putc('\t', fp);
751 }
752}
753
754/* little circular references here... */
755static void shtree(union node *n, int ind, char *pfx, FILE *fp);
756
757static void
758sharg(union node *arg, FILE *fp)
759{
760 char *p;
761 struct nodelist *bqlist;
762 int subtype;
763
764 if (arg->type != NARG) {
765 out1fmt("<node type %d>\n", arg->type);
766 abort();
767 }
768 bqlist = arg->narg.backquote;
769 for (p = arg->narg.text; *p; p++) {
770 switch (*p) {
771 case CTLESC:
772 putc(*++p, fp);
773 break;
774 case CTLVAR:
775 putc('$', fp);
776 putc('{', fp);
777 subtype = *++p;
778 if (subtype == VSLENGTH)
779 putc('#', fp);
780
781 while (*p != '=')
782 putc(*p++, fp);
783
784 if (subtype & VSNUL)
785 putc(':', fp);
786
787 switch (subtype & VSTYPE) {
788 case VSNORMAL:
789 putc('}', fp);
790 break;
791 case VSMINUS:
792 putc('-', fp);
793 break;
794 case VSPLUS:
795 putc('+', fp);
796 break;
797 case VSQUESTION:
798 putc('?', fp);
799 break;
800 case VSASSIGN:
801 putc('=', fp);
802 break;
803 case VSTRIMLEFT:
804 putc('#', fp);
805 break;
806 case VSTRIMLEFTMAX:
807 putc('#', fp);
808 putc('#', fp);
809 break;
810 case VSTRIMRIGHT:
811 putc('%', fp);
812 break;
813 case VSTRIMRIGHTMAX:
814 putc('%', fp);
815 putc('%', fp);
816 break;
817 case VSLENGTH:
818 break;
819 default:
820 out1fmt("<subtype %d>", subtype);
821 }
822 break;
823 case CTLENDVAR:
824 putc('}', fp);
825 break;
826 case CTLBACKQ:
827 case CTLBACKQ|CTLQUOTE:
828 putc('$', fp);
829 putc('(', fp);
830 shtree(bqlist->n, -1, NULL, fp);
831 putc(')', fp);
832 break;
833 default:
834 putc(*p, fp);
835 break;
836 }
837 }
838}
839
840static void
841shcmd(union node *cmd, FILE *fp)
842{
843 union node *np;
844 int first;
845 const char *s;
846 int dftfd;
847
848 first = 1;
849 for (np = cmd->ncmd.args; np; np = np->narg.next) {
850 if (! first)
851 putchar(' ');
852 sharg(np, fp);
853 first = 0;
854 }
855 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
856 if (! first)
857 putchar(' ');
858 switch (np->nfile.type) {
859 case NTO: s = ">"; dftfd = 1; break;
860 case NCLOBBER: s = ">|"; dftfd = 1; break;
861 case NAPPEND: s = ">>"; dftfd = 1; break;
862 case NTOFD: s = ">&"; dftfd = 1; break;
863 case NFROM: s = "<"; dftfd = 0; break;
864 case NFROMFD: s = "<&"; dftfd = 0; break;
865 case NFROMTO: s = "<>"; dftfd = 0; break;
866 default: s = "*error*"; dftfd = 0; break;
867 }
868 if (np->nfile.fd != dftfd)
869 fprintf(fp, "%d", np->nfile.fd);
870 fputs(s, fp);
871 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
872 fprintf(fp, "%d", np->ndup.dupfd);
873 } else {
874 sharg(np->nfile.fname, fp);
875 }
876 first = 0;
877 }
878}
879
880static void
881shtree(union node *n, int ind, char *pfx, FILE *fp)
882{
883 struct nodelist *lp;
884 const char *s;
885
886 if (n == NULL)
887 return;
888
889 indent(ind, pfx, fp);
890 switch (n->type) {
891 case NSEMI:
892 s = "; ";
893 goto binop;
894 case NAND:
895 s = " && ";
896 goto binop;
897 case NOR:
898 s = " || ";
899 binop:
900 shtree(n->nbinary.ch1, ind, NULL, fp);
901 /* if (ind < 0) */
902 fputs(s, fp);
903 shtree(n->nbinary.ch2, ind, NULL, fp);
904 break;
905 case NCMD:
906 shcmd(n, fp);
907 if (ind >= 0)
908 putc('\n', fp);
909 break;
910 case NPIPE:
911 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
912 shcmd(lp->n, fp);
913 if (lp->next)
914 fputs(" | ", fp);
915 }
916 if (n->npipe.backgnd)
917 fputs(" &", fp);
918 if (ind >= 0)
919 putc('\n', fp);
920 break;
921 default:
922 fprintf(fp, "<node type %d>", n->type);
923 if (ind >= 0)
924 putc('\n', fp);
925 break;
926 }
927}
928
929static void
930showtree(union node *n)
931{
932 trace_puts("showtree called\n");
933 shtree(n, 1, NULL, stdout);
934}
935
936#define TRACE(param) trace_printf param
937#define TRACEV(param) trace_vprintf param
938
939#else
940
941#define TRACE(param)
942#define TRACEV(param)
943
944#endif /* DEBUG */
945
946
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000947/* ============ Parser data */
948
949/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000950 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
951 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000952struct strlist {
953 struct strlist *next;
954 char *text;
955};
956
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000957#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000958struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000959#endif
960
Denis Vlasenkob012b102007-02-19 22:43:01 +0000961struct strpush {
962 struct strpush *prev; /* preceding string on stack */
963 char *prevstring;
964 int prevnleft;
965#if ENABLE_ASH_ALIAS
966 struct alias *ap; /* if push was associated with an alias */
967#endif
968 char *string; /* remember the string since it may change */
969};
970
971struct parsefile {
972 struct parsefile *prev; /* preceding file on stack */
973 int linno; /* current line */
974 int fd; /* file descriptor (or -1 if string) */
975 int nleft; /* number of chars left in this line */
976 int lleft; /* number of chars left in this buffer */
977 char *nextc; /* next char in buffer */
978 char *buf; /* input buffer */
979 struct strpush *strpush; /* for pushing strings at this level */
980 struct strpush basestrpush; /* so pushing one is fast */
981};
982
983static struct parsefile basepf; /* top level input file */
984static struct parsefile *parsefile = &basepf; /* current input file */
985static int startlinno; /* line # where last token started */
986static char *commandname; /* currently executing command */
987static struct strlist *cmdenviron; /* environment for builtin command */
988static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000989
990
991/* ============ Message printing */
992
993static void
994ash_vmsg(const char *msg, va_list ap)
995{
996 fprintf(stderr, "%s: ", arg0);
997 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +0000998 if (strcmp(arg0, commandname))
999 fprintf(stderr, "%s: ", commandname);
1000 if (!iflag || parsefile->fd)
1001 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001002 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001003 vfprintf(stderr, msg, ap);
1004 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001005}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001006
1007/*
1008 * Exverror is called to raise the error exception. If the second argument
1009 * is not NULL then error prints an error message using printf style
1010 * formatting. It then raises the error exception.
1011 */
1012static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1013static void
1014ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001015{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001016#if DEBUG
1017 if (msg) {
1018 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1019 TRACEV((msg, ap));
1020 TRACE(("\") pid=%d\n", getpid()));
1021 } else
1022 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1023 if (msg)
1024#endif
1025 ash_vmsg(msg, ap);
1026
1027 flush_stdout_stderr();
1028 raise_exception(cond);
1029 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001030}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001031
1032static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1033static void
1034ash_msg_and_raise_error(const char *msg, ...)
1035{
1036 va_list ap;
1037
1038 va_start(ap, msg);
1039 ash_vmsg_and_raise(EXERROR, msg, ap);
1040 /* NOTREACHED */
1041 va_end(ap);
1042}
1043
1044static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1045static void
1046ash_msg_and_raise(int cond, const char *msg, ...)
1047{
1048 va_list ap;
1049
1050 va_start(ap, msg);
1051 ash_vmsg_and_raise(cond, msg, ap);
1052 /* NOTREACHED */
1053 va_end(ap);
1054}
1055
1056/*
1057 * error/warning routines for external builtins
1058 */
1059static void
1060ash_msg(const char *fmt, ...)
1061{
1062 va_list ap;
1063
1064 va_start(ap, fmt);
1065 ash_vmsg(fmt, ap);
1066 va_end(ap);
1067}
1068
1069/*
1070 * Return a string describing an error. The returned string may be a
1071 * pointer to a static buffer that will be overwritten on the next call.
1072 * Action describes the operation that got the error.
1073 */
1074static const char *
1075errmsg(int e, const char *em)
1076{
1077 if (e == ENOENT || e == ENOTDIR) {
1078 return em;
1079 }
1080 return strerror(e);
1081}
1082
1083
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001084/* ============ Memory allocation */
1085
1086/*
1087 * It appears that grabstackstr() will barf with such alignments
1088 * because stalloc() will return a string allocated in a new stackblock.
1089 */
1090#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1091enum {
1092 /* Most machines require the value returned from malloc to be aligned
1093 * in some way. The following macro will get this right
1094 * on many machines. */
1095 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1096 /* Minimum size of a block */
1097 MINSIZE = SHELL_ALIGN(504),
1098};
1099
1100struct stack_block {
1101 struct stack_block *prev;
1102 char space[MINSIZE];
1103};
1104
1105struct stackmark {
1106 struct stack_block *stackp;
1107 char *stacknxt;
1108 size_t stacknleft;
1109 struct stackmark *marknext;
1110};
1111
1112static struct stack_block stackbase;
1113static struct stack_block *stackp = &stackbase;
1114static struct stackmark *markp;
1115static char *stacknxt = stackbase.space;
1116static size_t stacknleft = MINSIZE;
1117static char *sstrend = stackbase.space + MINSIZE;
1118static int herefd = -1;
1119
1120#define stackblock() ((void *)stacknxt)
1121#define stackblocksize() stacknleft
1122
1123static void *
1124ckrealloc(void * p, size_t nbytes)
1125{
1126 p = realloc(p, nbytes);
1127 if (!p)
1128 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1129 return p;
1130}
1131
1132static void *
1133ckmalloc(size_t nbytes)
1134{
1135 return ckrealloc(NULL, nbytes);
1136}
1137
1138/*
1139 * Make a copy of a string in safe storage.
1140 */
1141static char *
1142ckstrdup(const char *s)
1143{
1144 char *p = strdup(s);
1145 if (!p)
1146 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1147 return p;
1148}
1149
1150/*
1151 * Parse trees for commands are allocated in lifo order, so we use a stack
1152 * to make this more efficient, and also to avoid all sorts of exception
1153 * handling code to handle interrupts in the middle of a parse.
1154 *
1155 * The size 504 was chosen because the Ultrix malloc handles that size
1156 * well.
1157 */
1158static void *
1159stalloc(size_t nbytes)
1160{
1161 char *p;
1162 size_t aligned;
1163
1164 aligned = SHELL_ALIGN(nbytes);
1165 if (aligned > stacknleft) {
1166 size_t len;
1167 size_t blocksize;
1168 struct stack_block *sp;
1169
1170 blocksize = aligned;
1171 if (blocksize < MINSIZE)
1172 blocksize = MINSIZE;
1173 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1174 if (len < blocksize)
1175 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1176 INT_OFF;
1177 sp = ckmalloc(len);
1178 sp->prev = stackp;
1179 stacknxt = sp->space;
1180 stacknleft = blocksize;
1181 sstrend = stacknxt + blocksize;
1182 stackp = sp;
1183 INT_ON;
1184 }
1185 p = stacknxt;
1186 stacknxt += aligned;
1187 stacknleft -= aligned;
1188 return p;
1189}
1190
1191static void
1192stunalloc(void *p)
1193{
1194#if DEBUG
1195 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
1196 write(2, "stunalloc\n", 10);
1197 abort();
1198 }
1199#endif
1200 stacknleft += stacknxt - (char *)p;
1201 stacknxt = p;
1202}
1203
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001204/*
1205 * Like strdup but works with the ash stack.
1206 */
1207static char *
1208ststrdup(const char *p)
1209{
1210 size_t len = strlen(p) + 1;
1211 return memcpy(stalloc(len), p, len);
1212}
1213
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001214static void
1215setstackmark(struct stackmark *mark)
1216{
1217 mark->stackp = stackp;
1218 mark->stacknxt = stacknxt;
1219 mark->stacknleft = stacknleft;
1220 mark->marknext = markp;
1221 markp = mark;
1222}
1223
1224static void
1225popstackmark(struct stackmark *mark)
1226{
1227 struct stack_block *sp;
1228
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001229 if (!mark->stackp)
1230 return;
1231
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001232 INT_OFF;
1233 markp = mark->marknext;
1234 while (stackp != mark->stackp) {
1235 sp = stackp;
1236 stackp = sp->prev;
1237 free(sp);
1238 }
1239 stacknxt = mark->stacknxt;
1240 stacknleft = mark->stacknleft;
1241 sstrend = mark->stacknxt + mark->stacknleft;
1242 INT_ON;
1243}
1244
1245/*
1246 * When the parser reads in a string, it wants to stick the string on the
1247 * stack and only adjust the stack pointer when it knows how big the
1248 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1249 * of space on top of the stack and stackblocklen returns the length of
1250 * this block. Growstackblock will grow this space by at least one byte,
1251 * possibly moving it (like realloc). Grabstackblock actually allocates the
1252 * part of the block that has been used.
1253 */
1254static void
1255growstackblock(void)
1256{
1257 size_t newlen;
1258
1259 newlen = stacknleft * 2;
1260 if (newlen < stacknleft)
1261 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1262 if (newlen < 128)
1263 newlen += 128;
1264
1265 if (stacknxt == stackp->space && stackp != &stackbase) {
1266 struct stack_block *oldstackp;
1267 struct stackmark *xmark;
1268 struct stack_block *sp;
1269 struct stack_block *prevstackp;
1270 size_t grosslen;
1271
1272 INT_OFF;
1273 oldstackp = stackp;
1274 sp = stackp;
1275 prevstackp = sp->prev;
1276 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1277 sp = ckrealloc(sp, grosslen);
1278 sp->prev = prevstackp;
1279 stackp = sp;
1280 stacknxt = sp->space;
1281 stacknleft = newlen;
1282 sstrend = sp->space + newlen;
1283
1284 /*
1285 * Stack marks pointing to the start of the old block
1286 * must be relocated to point to the new block
1287 */
1288 xmark = markp;
1289 while (xmark != NULL && xmark->stackp == oldstackp) {
1290 xmark->stackp = stackp;
1291 xmark->stacknxt = stacknxt;
1292 xmark->stacknleft = stacknleft;
1293 xmark = xmark->marknext;
1294 }
1295 INT_ON;
1296 } else {
1297 char *oldspace = stacknxt;
1298 int oldlen = stacknleft;
1299 char *p = stalloc(newlen);
1300
1301 /* free the space we just allocated */
1302 stacknxt = memcpy(p, oldspace, oldlen);
1303 stacknleft += newlen;
1304 }
1305}
1306
1307static void
1308grabstackblock(size_t len)
1309{
1310 len = SHELL_ALIGN(len);
1311 stacknxt += len;
1312 stacknleft -= len;
1313}
1314
1315/*
1316 * The following routines are somewhat easier to use than the above.
1317 * The user declares a variable of type STACKSTR, which may be declared
1318 * to be a register. The macro STARTSTACKSTR initializes things. Then
1319 * the user uses the macro STPUTC to add characters to the string. In
1320 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1321 * grown as necessary. When the user is done, she can just leave the
1322 * string there and refer to it using stackblock(). Or she can allocate
1323 * the space for it using grabstackstr(). If it is necessary to allow
1324 * someone else to use the stack temporarily and then continue to grow
1325 * the string, the user should use grabstack to allocate the space, and
1326 * then call ungrabstr(p) to return to the previous mode of operation.
1327 *
1328 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1329 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1330 * is space for at least one character.
1331 */
1332static void *
1333growstackstr(void)
1334{
1335 size_t len = stackblocksize();
1336 if (herefd >= 0 && len >= 1024) {
1337 full_write(herefd, stackblock(), len);
1338 return stackblock();
1339 }
1340 growstackblock();
1341 return stackblock() + len;
1342}
1343
1344/*
1345 * Called from CHECKSTRSPACE.
1346 */
1347static char *
1348makestrspace(size_t newlen, char *p)
1349{
1350 size_t len = p - stacknxt;
1351 size_t size = stackblocksize();
1352
1353 for (;;) {
1354 size_t nleft;
1355
1356 size = stackblocksize();
1357 nleft = size - len;
1358 if (nleft >= newlen)
1359 break;
1360 growstackblock();
1361 }
1362 return stackblock() + len;
1363}
1364
1365static char *
1366stack_nputstr(const char *s, size_t n, char *p)
1367{
1368 p = makestrspace(n, p);
1369 p = memcpy(p, s, n) + n;
1370 return p;
1371}
1372
1373static char *
1374stack_putstr(const char *s, char *p)
1375{
1376 return stack_nputstr(s, strlen(s), p);
1377}
1378
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001379static char *
1380_STPUTC(int c, char *p)
1381{
1382 if (p == sstrend)
1383 p = growstackstr();
1384 *p++ = c;
1385 return p;
1386}
1387
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001388#define STARTSTACKSTR(p) ((p) = stackblock())
1389#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001390#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001391 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001392 char *q = (p); \
1393 size_t l = (n); \
1394 size_t m = sstrend - q; \
1395 if (l > m) \
1396 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001397 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001398#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001399#define STACKSTRNUL(p) \
1400 do { \
1401 if ((p) == sstrend) \
1402 p = growstackstr(); \
1403 *p = '\0'; \
1404 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001405#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001406#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001407#define STADJUST(amount, p) (p += (amount))
1408
1409#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1410#define ungrabstackstr(s, p) stunalloc((s))
1411#define stackstrend() ((void *)sstrend)
1412
1413
1414/* ============ String helpers */
1415
1416/*
1417 * prefix -- see if pfx is a prefix of string.
1418 */
1419static char *
1420prefix(const char *string, const char *pfx)
1421{
1422 while (*pfx) {
1423 if (*pfx++ != *string++)
1424 return 0;
1425 }
1426 return (char *) string;
1427}
1428
1429/*
1430 * Check for a valid number. This should be elsewhere.
1431 */
1432static int
1433is_number(const char *p)
1434{
1435 do {
1436 if (!isdigit(*p))
1437 return 0;
1438 } while (*++p != '\0');
1439 return 1;
1440}
1441
1442/*
1443 * Convert a string of digits to an integer, printing an error message on
1444 * failure.
1445 */
1446static int
1447number(const char *s)
1448{
1449 if (!is_number(s))
1450 ash_msg_and_raise_error(illnum, s);
1451 return atoi(s);
1452}
1453
1454/*
1455 * Produce a possibly single quoted string suitable as input to the shell.
1456 * The return string is allocated on the stack.
1457 */
1458static char *
1459single_quote(const char *s)
1460{
1461 char *p;
1462
1463 STARTSTACKSTR(p);
1464
1465 do {
1466 char *q;
1467 size_t len;
1468
1469 len = strchrnul(s, '\'') - s;
1470
1471 q = p = makestrspace(len + 3, p);
1472
1473 *q++ = '\'';
1474 q = memcpy(q, s, len) + len;
1475 *q++ = '\'';
1476 s += len;
1477
1478 STADJUST(q - p, p);
1479
1480 len = strspn(s, "'");
1481 if (!len)
1482 break;
1483
1484 q = p = makestrspace(len + 3, p);
1485
1486 *q++ = '"';
1487 q = memcpy(q, s, len) + len;
1488 *q++ = '"';
1489 s += len;
1490
1491 STADJUST(q - p, p);
1492 } while (*s);
1493
1494 USTPUTC(0, p);
1495
1496 return stackblock();
1497}
1498
1499
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001500/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001501
1502static char **argptr; /* argument list for builtin commands */
1503static char *optionarg; /* set by nextopt (like getopt) */
1504static char *optptr; /* used by nextopt */
1505
1506/*
1507 * XXX - should get rid of. have all builtins use getopt(3). the
1508 * library getopt must have the BSD extension static variable "optreset"
1509 * otherwise it can't be used within the shell safely.
1510 *
1511 * Standard option processing (a la getopt) for builtin routines. The
1512 * only argument that is passed to nextopt is the option string; the
1513 * other arguments are unnecessary. It return the character, or '\0' on
1514 * end of input.
1515 */
1516static int
1517nextopt(const char *optstring)
1518{
1519 char *p;
1520 const char *q;
1521 char c;
1522
1523 p = optptr;
1524 if (p == NULL || *p == '\0') {
1525 p = *argptr;
1526 if (p == NULL || *p != '-' || *++p == '\0')
1527 return '\0';
1528 argptr++;
1529 if (LONE_DASH(p)) /* check for "--" */
1530 return '\0';
1531 }
1532 c = *p++;
1533 for (q = optstring; *q != c; ) {
1534 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001535 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001536 if (*++q == ':')
1537 q++;
1538 }
1539 if (*++q == ':') {
1540 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001541 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001542 optionarg = p;
1543 p = NULL;
1544 }
1545 optptr = p;
1546 return c;
1547}
1548
1549
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001550/* ============ Math support definitions */
1551
1552#if ENABLE_ASH_MATH_SUPPORT_64
1553typedef int64_t arith_t;
1554#define arith_t_type long long
1555#else
1556typedef long arith_t;
1557#define arith_t_type long
1558#endif
1559
1560#if ENABLE_ASH_MATH_SUPPORT
1561static arith_t dash_arith(const char *);
1562static arith_t arith(const char *expr, int *perrcode);
1563#endif
1564
1565#if ENABLE_ASH_RANDOM_SUPPORT
1566static unsigned long rseed;
1567#ifndef DYNAMIC_VAR
1568#define DYNAMIC_VAR
1569#endif
1570#endif
1571
1572
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001573/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001574
1575/* flags */
1576#define VEXPORT 0x01 /* variable is exported */
1577#define VREADONLY 0x02 /* variable cannot be modified */
1578#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1579#define VTEXTFIXED 0x08 /* text is statically allocated */
1580#define VSTACK 0x10 /* text is allocated on the stack */
1581#define VUNSET 0x20 /* the variable is not set */
1582#define VNOFUNC 0x40 /* don't call the callback function */
1583#define VNOSET 0x80 /* do not set variable - just readonly test */
1584#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1585#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001586# define VDYNAMIC 0x200 /* dynamic variable */
1587#else
1588# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001589#endif
1590
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001591static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1592#ifdef IFS_BROKEN
1593static const char defifsvar[] = "IFS= \t\n";
1594#define defifs (defifsvar + 4)
1595#else
1596static const char defifs[] = " \t\n";
1597#endif
1598
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001599struct shparam {
1600 int nparam; /* # of positional parameters (without $0) */
1601 unsigned char malloc; /* if parameter list dynamically allocated */
1602 char **p; /* parameter list */
1603#if ENABLE_ASH_GETOPTS
1604 int optind; /* next parameter to be processed by getopts */
1605 int optoff; /* used by getopts */
1606#endif
1607};
1608
1609static struct shparam shellparam; /* $@ current positional parameters */
1610
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00001611/*
1612 * Free the list of positional parameters.
1613 */
1614static void
1615freeparam(volatile struct shparam *param)
1616{
1617 char **ap;
1618
1619 if (param->malloc) {
1620 for (ap = param->p; *ap; ap++)
1621 free(*ap);
1622 free(param->p);
1623 }
1624}
1625
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001626#if ENABLE_ASH_GETOPTS
1627static void
1628getoptsreset(const char *value)
1629{
1630 shellparam.optind = number(value);
1631 shellparam.optoff = -1;
1632}
1633#endif
1634
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001635struct var {
1636 struct var *next; /* next entry in hash list */
1637 int flags; /* flags are defined above */
1638 const char *text; /* name=value */
1639 void (*func)(const char *); /* function to be called when */
1640 /* the variable gets set/unset */
1641};
1642
1643struct localvar {
1644 struct localvar *next; /* next local variable in list */
1645 struct var *vp; /* the variable that was made local */
1646 int flags; /* saved flags */
1647 const char *text; /* saved text */
1648};
1649
1650/* Forward decls for varinit[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001651#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001652static void
1653change_lc_all(const char *value)
1654{
1655 if (value && *value != '\0')
1656 setlocale(LC_ALL, value);
1657}
1658static void
1659change_lc_ctype(const char *value)
1660{
1661 if (value && *value != '\0')
1662 setlocale(LC_CTYPE, value);
1663}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001664#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001665#if ENABLE_ASH_MAIL
1666static void chkmail(void);
1667static void changemail(const char *);
1668#endif
1669static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001670#if ENABLE_ASH_RANDOM_SUPPORT
1671static void change_random(const char *);
1672#endif
1673
1674static struct var varinit[] = {
1675#ifdef IFS_BROKEN
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001676 { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001677#else
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001678 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001679#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001680#if ENABLE_ASH_MAIL
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001681 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1682 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001683#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001684 { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1685 { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
1686 { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
1687 { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001688#if ENABLE_ASH_GETOPTS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001689 { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001690#endif
1691#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001692 { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001693#endif
1694#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001695 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1696 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001697#endif
1698#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001699 { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001700#endif
1701};
1702
1703#define vifs varinit[0]
1704#if ENABLE_ASH_MAIL
1705#define vmail (&vifs)[1]
1706#define vmpath (&vmail)[1]
1707#else
1708#define vmpath vifs
1709#endif
1710#define vpath (&vmpath)[1]
1711#define vps1 (&vpath)[1]
1712#define vps2 (&vps1)[1]
1713#define vps4 (&vps2)[1]
1714#define voptind (&vps4)[1]
1715#if ENABLE_ASH_GETOPTS
1716#define vrandom (&voptind)[1]
1717#else
1718#define vrandom (&vps4)[1]
1719#endif
1720#define defpath (defpathvar + 5)
1721
1722/*
1723 * The following macros access the values of the above variables.
1724 * They have to skip over the name. They return the null string
1725 * for unset variables.
1726 */
1727#define ifsval() (vifs.text + 4)
1728#define ifsset() ((vifs.flags & VUNSET) == 0)
1729#define mailval() (vmail.text + 5)
1730#define mpathval() (vmpath.text + 9)
1731#define pathval() (vpath.text + 5)
1732#define ps1val() (vps1.text + 4)
1733#define ps2val() (vps2.text + 4)
1734#define ps4val() (vps4.text + 4)
1735#define optindval() (voptind.text + 7)
1736
1737#define mpathset() ((vmpath.flags & VUNSET) == 0)
1738
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001739/*
1740 * The parsefile structure pointed to by the global variable parsefile
1741 * contains information about the current file being read.
1742 */
1743struct redirtab {
1744 struct redirtab *next;
1745 int renamed[10];
1746 int nullredirs;
1747};
1748
1749static struct redirtab *redirlist;
1750static int nullredirs;
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{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003522 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3523 int i = 1;
3524 do {
3525 if (argv[i][0] == '%') {
3526 struct job *jp = getjob(argv[i], 0);
3527 unsigned pid = jp->ps[0].pid;
3528 /* Enough space for ' -NNN<nul>' */
3529 argv[i] = alloca(sizeof(int)*3 + 3);
3530 /* kill_main has matching code to expect
3531 * leading space. Needed to not confuse
3532 * negative pids with "kill -SIGNAL_NO" syntax */
3533 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003534 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003535 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003536 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003537 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003538}
3539
3540static void
3541showpipe(struct job *jp, FILE *out)
3542{
3543 struct procstat *sp;
3544 struct procstat *spend;
3545
3546 spend = jp->ps + jp->nprocs;
3547 for (sp = jp->ps + 1; sp < spend; sp++)
3548 fprintf(out, " | %s", sp->cmd);
3549 outcslow('\n', out);
3550 flush_stdout_stderr();
3551}
3552
3553
3554static int
3555restartjob(struct job *jp, int mode)
3556{
3557 struct procstat *ps;
3558 int i;
3559 int status;
3560 pid_t pgid;
3561
3562 INT_OFF;
3563 if (jp->state == JOBDONE)
3564 goto out;
3565 jp->state = JOBRUNNING;
3566 pgid = jp->ps->pid;
3567 if (mode == FORK_FG)
3568 xtcsetpgrp(ttyfd, pgid);
3569 killpg(pgid, SIGCONT);
3570 ps = jp->ps;
3571 i = jp->nprocs;
3572 do {
3573 if (WIFSTOPPED(ps->status)) {
3574 ps->status = -1;
3575 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003576 ps++;
3577 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003578 out:
3579 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3580 INT_ON;
3581 return status;
3582}
3583
3584static int
3585fg_bgcmd(int argc, char **argv)
3586{
3587 struct job *jp;
3588 FILE *out;
3589 int mode;
3590 int retval;
3591
3592 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3593 nextopt(nullstr);
3594 argv = argptr;
3595 out = stdout;
3596 do {
3597 jp = getjob(*argv, 1);
3598 if (mode == FORK_BG) {
3599 set_curjob(jp, CUR_RUNNING);
3600 fprintf(out, "[%d] ", jobno(jp));
3601 }
3602 outstr(jp->ps->cmd, out);
3603 showpipe(jp, out);
3604 retval = restartjob(jp, mode);
3605 } while (*argv && *++argv);
3606 return retval;
3607}
3608#endif
3609
3610static int
3611sprint_status(char *s, int status, int sigonly)
3612{
3613 int col;
3614 int st;
3615
3616 col = 0;
3617 if (!WIFEXITED(status)) {
3618#if JOBS
3619 if (WIFSTOPPED(status))
3620 st = WSTOPSIG(status);
3621 else
3622#endif
3623 st = WTERMSIG(status);
3624 if (sigonly) {
3625 if (st == SIGINT || st == SIGPIPE)
3626 goto out;
3627#if JOBS
3628 if (WIFSTOPPED(status))
3629 goto out;
3630#endif
3631 }
3632 st &= 0x7f;
3633 col = fmtstr(s, 32, strsignal(st));
3634 if (WCOREDUMP(status)) {
3635 col += fmtstr(s + col, 16, " (core dumped)");
3636 }
3637 } else if (!sigonly) {
3638 st = WEXITSTATUS(status);
3639 if (st)
3640 col = fmtstr(s, 16, "Done(%d)", st);
3641 else
3642 col = fmtstr(s, 16, "Done");
3643 }
3644 out:
3645 return col;
3646}
3647
3648/*
3649 * Do a wait system call. If job control is compiled in, we accept
3650 * stopped processes. If block is zero, we return a value of zero
3651 * rather than blocking.
3652 *
3653 * System V doesn't have a non-blocking wait system call. It does
3654 * have a SIGCLD signal that is sent to a process when one of it's
3655 * children dies. The obvious way to use SIGCLD would be to install
3656 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3657 * was received, and have waitproc bump another counter when it got
3658 * the status of a process. Waitproc would then know that a wait
3659 * system call would not block if the two counters were different.
3660 * This approach doesn't work because if a process has children that
3661 * have not been waited for, System V will send it a SIGCLD when it
3662 * installs a signal handler for SIGCLD. What this means is that when
3663 * a child exits, the shell will be sent SIGCLD signals continuously
3664 * until is runs out of stack space, unless it does a wait call before
3665 * restoring the signal handler. The code below takes advantage of
3666 * this (mis)feature by installing a signal handler for SIGCLD and
3667 * then checking to see whether it was called. If there are any
3668 * children to be waited for, it will be.
3669 *
3670 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3671 * waits at all. In this case, the user will not be informed when
3672 * a background process until the next time she runs a real program
3673 * (as opposed to running a builtin command or just typing return),
3674 * and the jobs command may give out of date information.
3675 */
3676static int
3677waitproc(int block, int *status)
3678{
3679 int flags = 0;
3680
3681#if JOBS
3682 if (jobctl)
3683 flags |= WUNTRACED;
3684#endif
3685 if (block == 0)
3686 flags |= WNOHANG;
3687 return wait3(status, flags, (struct rusage *)NULL);
3688}
3689
3690/*
3691 * Wait for a process to terminate.
3692 */
3693static int
3694dowait(int block, struct job *job)
3695{
3696 int pid;
3697 int status;
3698 struct job *jp;
3699 struct job *thisjob;
3700 int state;
3701
3702 TRACE(("dowait(%d) called\n", block));
3703 pid = waitproc(block, &status);
3704 TRACE(("wait returns pid %d, status=%d\n", pid, status));
3705 if (pid <= 0)
3706 return pid;
3707 INT_OFF;
3708 thisjob = NULL;
3709 for (jp = curjob; jp; jp = jp->prev_job) {
3710 struct procstat *sp;
3711 struct procstat *spend;
3712 if (jp->state == JOBDONE)
3713 continue;
3714 state = JOBDONE;
3715 spend = jp->ps + jp->nprocs;
3716 sp = jp->ps;
3717 do {
3718 if (sp->pid == pid) {
3719 TRACE(("Job %d: changing status of proc %d "
3720 "from 0x%x to 0x%x\n",
3721 jobno(jp), pid, sp->status, status));
3722 sp->status = status;
3723 thisjob = jp;
3724 }
3725 if (sp->status == -1)
3726 state = JOBRUNNING;
3727#if JOBS
3728 if (state == JOBRUNNING)
3729 continue;
3730 if (WIFSTOPPED(sp->status)) {
3731 jp->stopstatus = sp->status;
3732 state = JOBSTOPPED;
3733 }
3734#endif
3735 } while (++sp < spend);
3736 if (thisjob)
3737 goto gotjob;
3738 }
3739#if JOBS
3740 if (!WIFSTOPPED(status))
3741#endif
3742
3743 jobless--;
3744 goto out;
3745
3746 gotjob:
3747 if (state != JOBRUNNING) {
3748 thisjob->changed = 1;
3749
3750 if (thisjob->state != state) {
3751 TRACE(("Job %d: changing state from %d to %d\n",
3752 jobno(thisjob), thisjob->state, state));
3753 thisjob->state = state;
3754#if JOBS
3755 if (state == JOBSTOPPED) {
3756 set_curjob(thisjob, CUR_STOPPED);
3757 }
3758#endif
3759 }
3760 }
3761
3762 out:
3763 INT_ON;
3764
3765 if (thisjob && thisjob == job) {
3766 char s[48 + 1];
3767 int len;
3768
3769 len = sprint_status(s, status, 1);
3770 if (len) {
3771 s[len] = '\n';
3772 s[len + 1] = 0;
3773 out2str(s);
3774 }
3775 }
3776 return pid;
3777}
3778
3779#if JOBS
3780static void
3781showjob(FILE *out, struct job *jp, int mode)
3782{
3783 struct procstat *ps;
3784 struct procstat *psend;
3785 int col;
3786 int indent;
3787 char s[80];
3788
3789 ps = jp->ps;
3790
3791 if (mode & SHOW_PGID) {
3792 /* just output process (group) id of pipeline */
3793 fprintf(out, "%d\n", ps->pid);
3794 return;
3795 }
3796
3797 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3798 indent = col;
3799
3800 if (jp == curjob)
3801 s[col - 2] = '+';
3802 else if (curjob && jp == curjob->prev_job)
3803 s[col - 2] = '-';
3804
3805 if (mode & SHOW_PID)
3806 col += fmtstr(s + col, 16, "%d ", ps->pid);
3807
3808 psend = ps + jp->nprocs;
3809
3810 if (jp->state == JOBRUNNING) {
3811 strcpy(s + col, "Running");
3812 col += sizeof("Running") - 1;
3813 } else {
3814 int status = psend[-1].status;
3815 if (jp->state == JOBSTOPPED)
3816 status = jp->stopstatus;
3817 col += sprint_status(s + col, status, 0);
3818 }
3819
3820 goto start;
3821
3822 do {
3823 /* for each process */
3824 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
3825 start:
3826 fprintf(out, "%s%*c%s",
3827 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3828 );
3829 if (!(mode & SHOW_PID)) {
3830 showpipe(jp, out);
3831 break;
3832 }
3833 if (++ps == psend) {
3834 outcslow('\n', out);
3835 break;
3836 }
3837 } while (1);
3838
3839 jp->changed = 0;
3840
3841 if (jp->state == JOBDONE) {
3842 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3843 freejob(jp);
3844 }
3845}
3846
3847static int
3848jobscmd(int argc, char **argv)
3849{
3850 int mode, m;
3851 FILE *out;
3852
3853 mode = 0;
3854 while ((m = nextopt("lp"))) {
3855 if (m == 'l')
3856 mode = SHOW_PID;
3857 else
3858 mode = SHOW_PGID;
3859 }
3860
3861 out = stdout;
3862 argv = argptr;
3863 if (*argv) {
3864 do
3865 showjob(out, getjob(*argv,0), mode);
3866 while (*++argv);
3867 } else
3868 showjobs(out, mode);
3869
3870 return 0;
3871}
3872
3873/*
3874 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3875 * statuses have changed since the last call to showjobs.
3876 */
3877static void
3878showjobs(FILE *out, int mode)
3879{
3880 struct job *jp;
3881
3882 TRACE(("showjobs(%x) called\n", mode));
3883
3884 /* If not even one one job changed, there is nothing to do */
3885 while (dowait(DOWAIT_NORMAL, NULL) > 0)
3886 continue;
3887
3888 for (jp = curjob; jp; jp = jp->prev_job) {
3889 if (!(mode & SHOW_CHANGED) || jp->changed)
3890 showjob(out, jp, mode);
3891 }
3892}
3893#endif /* JOBS */
3894
3895static int
3896getstatus(struct job *job)
3897{
3898 int status;
3899 int retval;
3900
3901 status = job->ps[job->nprocs - 1].status;
3902 retval = WEXITSTATUS(status);
3903 if (!WIFEXITED(status)) {
3904#if JOBS
3905 retval = WSTOPSIG(status);
3906 if (!WIFSTOPPED(status))
3907#endif
3908 {
3909 /* XXX: limits number of signals */
3910 retval = WTERMSIG(status);
3911#if JOBS
3912 if (retval == SIGINT)
3913 job->sigint = 1;
3914#endif
3915 }
3916 retval += 128;
3917 }
3918 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3919 jobno(job), job->nprocs, status, retval));
3920 return retval;
3921}
3922
3923static int
3924waitcmd(int argc, char **argv)
3925{
3926 struct job *job;
3927 int retval;
3928 struct job *jp;
3929
3930 EXSIGON;
3931
3932 nextopt(nullstr);
3933 retval = 0;
3934
3935 argv = argptr;
3936 if (!*argv) {
3937 /* wait for all jobs */
3938 for (;;) {
3939 jp = curjob;
3940 while (1) {
3941 if (!jp) {
3942 /* no running procs */
3943 goto out;
3944 }
3945 if (jp->state == JOBRUNNING)
3946 break;
3947 jp->waited = 1;
3948 jp = jp->prev_job;
3949 }
3950 dowait(DOWAIT_BLOCK, 0);
3951 }
3952 }
3953
3954 retval = 127;
3955 do {
3956 if (**argv != '%') {
3957 pid_t pid = number(*argv);
3958 job = curjob;
3959 goto start;
3960 do {
3961 if (job->ps[job->nprocs - 1].pid == pid)
3962 break;
3963 job = job->prev_job;
3964 start:
3965 if (!job)
3966 goto repeat;
3967 } while (1);
3968 } else
3969 job = getjob(*argv, 0);
3970 /* loop until process terminated or stopped */
3971 while (job->state == JOBRUNNING)
3972 dowait(DOWAIT_BLOCK, 0);
3973 job->waited = 1;
3974 retval = getstatus(job);
3975 repeat:
3976 ;
3977 } while (*++argv);
3978
3979 out:
3980 return retval;
3981}
3982
3983static struct job *
3984growjobtab(void)
3985{
3986 size_t len;
3987 ptrdiff_t offset;
3988 struct job *jp, *jq;
3989
3990 len = njobs * sizeof(*jp);
3991 jq = jobtab;
3992 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
3993
3994 offset = (char *)jp - (char *)jq;
3995 if (offset) {
3996 /* Relocate pointers */
3997 size_t l = len;
3998
3999 jq = (struct job *)((char *)jq + l);
4000 while (l) {
4001 l -= sizeof(*jp);
4002 jq--;
4003#define joff(p) ((struct job *)((char *)(p) + l))
4004#define jmove(p) (p) = (void *)((char *)(p) + offset)
4005 if (joff(jp)->ps == &jq->ps0)
4006 jmove(joff(jp)->ps);
4007 if (joff(jp)->prev_job)
4008 jmove(joff(jp)->prev_job);
4009 }
4010 if (curjob)
4011 jmove(curjob);
4012#undef joff
4013#undef jmove
4014 }
4015
4016 njobs += 4;
4017 jobtab = jp;
4018 jp = (struct job *)((char *)jp + len);
4019 jq = jp + 3;
4020 do {
4021 jq->used = 0;
4022 } while (--jq >= jp);
4023 return jp;
4024}
4025
4026/*
4027 * Return a new job structure.
4028 * Called with interrupts off.
4029 */
4030static struct job *
4031makejob(union node *node, int nprocs)
4032{
4033 int i;
4034 struct job *jp;
4035
4036 for (i = njobs, jp = jobtab; ; jp++) {
4037 if (--i < 0) {
4038 jp = growjobtab();
4039 break;
4040 }
4041 if (jp->used == 0)
4042 break;
4043 if (jp->state != JOBDONE || !jp->waited)
4044 continue;
4045#if JOBS
4046 if (jobctl)
4047 continue;
4048#endif
4049 freejob(jp);
4050 break;
4051 }
4052 memset(jp, 0, sizeof(*jp));
4053#if JOBS
4054 if (jobctl)
4055 jp->jobctl = 1;
4056#endif
4057 jp->prev_job = curjob;
4058 curjob = jp;
4059 jp->used = 1;
4060 jp->ps = &jp->ps0;
4061 if (nprocs > 1) {
4062 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4063 }
4064 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4065 jobno(jp)));
4066 return jp;
4067}
4068
4069#if JOBS
4070/*
4071 * Return a string identifying a command (to be printed by the
4072 * jobs command).
4073 */
4074static char *cmdnextc;
4075
4076static void
4077cmdputs(const char *s)
4078{
4079 const char *p, *str;
4080 char c, cc[2] = " ";
4081 char *nextc;
4082 int subtype = 0;
4083 int quoted = 0;
4084 static const char vstype[VSTYPE + 1][4] = {
4085 "", "}", "-", "+", "?", "=",
4086 "%", "%%", "#", "##"
4087 };
4088
4089 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4090 p = s;
4091 while ((c = *p++) != 0) {
4092 str = 0;
4093 switch (c) {
4094 case CTLESC:
4095 c = *p++;
4096 break;
4097 case CTLVAR:
4098 subtype = *p++;
4099 if ((subtype & VSTYPE) == VSLENGTH)
4100 str = "${#";
4101 else
4102 str = "${";
4103 if (!(subtype & VSQUOTE) == !(quoted & 1))
4104 goto dostr;
4105 quoted ^= 1;
4106 c = '"';
4107 break;
4108 case CTLENDVAR:
4109 str = "\"}" + !(quoted & 1);
4110 quoted >>= 1;
4111 subtype = 0;
4112 goto dostr;
4113 case CTLBACKQ:
4114 str = "$(...)";
4115 goto dostr;
4116 case CTLBACKQ+CTLQUOTE:
4117 str = "\"$(...)\"";
4118 goto dostr;
4119#if ENABLE_ASH_MATH_SUPPORT
4120 case CTLARI:
4121 str = "$((";
4122 goto dostr;
4123 case CTLENDARI:
4124 str = "))";
4125 goto dostr;
4126#endif
4127 case CTLQUOTEMARK:
4128 quoted ^= 1;
4129 c = '"';
4130 break;
4131 case '=':
4132 if (subtype == 0)
4133 break;
4134 if ((subtype & VSTYPE) != VSNORMAL)
4135 quoted <<= 1;
4136 str = vstype[subtype & VSTYPE];
4137 if (subtype & VSNUL)
4138 c = ':';
4139 else
4140 goto checkstr;
4141 break;
4142 case '\'':
4143 case '\\':
4144 case '"':
4145 case '$':
4146 /* These can only happen inside quotes */
4147 cc[0] = c;
4148 str = cc;
4149 c = '\\';
4150 break;
4151 default:
4152 break;
4153 }
4154 USTPUTC(c, nextc);
4155 checkstr:
4156 if (!str)
4157 continue;
4158 dostr:
4159 while ((c = *str++)) {
4160 USTPUTC(c, nextc);
4161 }
4162 }
4163 if (quoted & 1) {
4164 USTPUTC('"', nextc);
4165 }
4166 *nextc = 0;
4167 cmdnextc = nextc;
4168}
4169
4170/* cmdtxt() and cmdlist() call each other */
4171static void cmdtxt(union node *n);
4172
4173static void
4174cmdlist(union node *np, int sep)
4175{
4176 for (; np; np = np->narg.next) {
4177 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004178 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179 cmdtxt(np);
4180 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004181 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004182 }
4183}
4184
4185static void
4186cmdtxt(union node *n)
4187{
4188 union node *np;
4189 struct nodelist *lp;
4190 const char *p;
4191 char s[2];
4192
4193 if (!n)
4194 return;
4195 switch (n->type) {
4196 default:
4197#if DEBUG
4198 abort();
4199#endif
4200 case NPIPE:
4201 lp = n->npipe.cmdlist;
4202 for (;;) {
4203 cmdtxt(lp->n);
4204 lp = lp->next;
4205 if (!lp)
4206 break;
4207 cmdputs(" | ");
4208 }
4209 break;
4210 case NSEMI:
4211 p = "; ";
4212 goto binop;
4213 case NAND:
4214 p = " && ";
4215 goto binop;
4216 case NOR:
4217 p = " || ";
4218 binop:
4219 cmdtxt(n->nbinary.ch1);
4220 cmdputs(p);
4221 n = n->nbinary.ch2;
4222 goto donode;
4223 case NREDIR:
4224 case NBACKGND:
4225 n = n->nredir.n;
4226 goto donode;
4227 case NNOT:
4228 cmdputs("!");
4229 n = n->nnot.com;
4230 donode:
4231 cmdtxt(n);
4232 break;
4233 case NIF:
4234 cmdputs("if ");
4235 cmdtxt(n->nif.test);
4236 cmdputs("; then ");
4237 n = n->nif.ifpart;
4238 if (n->nif.elsepart) {
4239 cmdtxt(n);
4240 cmdputs("; else ");
4241 n = n->nif.elsepart;
4242 }
4243 p = "; fi";
4244 goto dotail;
4245 case NSUBSHELL:
4246 cmdputs("(");
4247 n = n->nredir.n;
4248 p = ")";
4249 goto dotail;
4250 case NWHILE:
4251 p = "while ";
4252 goto until;
4253 case NUNTIL:
4254 p = "until ";
4255 until:
4256 cmdputs(p);
4257 cmdtxt(n->nbinary.ch1);
4258 n = n->nbinary.ch2;
4259 p = "; done";
4260 dodo:
4261 cmdputs("; do ");
4262 dotail:
4263 cmdtxt(n);
4264 goto dotail2;
4265 case NFOR:
4266 cmdputs("for ");
4267 cmdputs(n->nfor.var);
4268 cmdputs(" in ");
4269 cmdlist(n->nfor.args, 1);
4270 n = n->nfor.body;
4271 p = "; done";
4272 goto dodo;
4273 case NDEFUN:
4274 cmdputs(n->narg.text);
4275 p = "() { ... }";
4276 goto dotail2;
4277 case NCMD:
4278 cmdlist(n->ncmd.args, 1);
4279 cmdlist(n->ncmd.redirect, 0);
4280 break;
4281 case NARG:
4282 p = n->narg.text;
4283 dotail2:
4284 cmdputs(p);
4285 break;
4286 case NHERE:
4287 case NXHERE:
4288 p = "<<...";
4289 goto dotail2;
4290 case NCASE:
4291 cmdputs("case ");
4292 cmdputs(n->ncase.expr->narg.text);
4293 cmdputs(" in ");
4294 for (np = n->ncase.cases; np; np = np->nclist.next) {
4295 cmdtxt(np->nclist.pattern);
4296 cmdputs(") ");
4297 cmdtxt(np->nclist.body);
4298 cmdputs(";; ");
4299 }
4300 p = "esac";
4301 goto dotail2;
4302 case NTO:
4303 p = ">";
4304 goto redir;
4305 case NCLOBBER:
4306 p = ">|";
4307 goto redir;
4308 case NAPPEND:
4309 p = ">>";
4310 goto redir;
4311 case NTOFD:
4312 p = ">&";
4313 goto redir;
4314 case NFROM:
4315 p = "<";
4316 goto redir;
4317 case NFROMFD:
4318 p = "<&";
4319 goto redir;
4320 case NFROMTO:
4321 p = "<>";
4322 redir:
4323 s[0] = n->nfile.fd + '0';
4324 s[1] = '\0';
4325 cmdputs(s);
4326 cmdputs(p);
4327 if (n->type == NTOFD || n->type == NFROMFD) {
4328 s[0] = n->ndup.dupfd + '0';
4329 p = s;
4330 goto dotail2;
4331 }
4332 n = n->nfile.fname;
4333 goto donode;
4334 }
4335}
4336
4337static char *
4338commandtext(union node *n)
4339{
4340 char *name;
4341
4342 STARTSTACKSTR(cmdnextc);
4343 cmdtxt(n);
4344 name = stackblock();
4345 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4346 name, cmdnextc, cmdnextc));
4347 return ckstrdup(name);
4348}
4349#endif /* JOBS */
4350
4351/*
4352 * Fork off a subshell. If we are doing job control, give the subshell its
4353 * own process group. Jp is a job structure that the job is to be added to.
4354 * N is the command that will be evaluated by the child. Both jp and n may
4355 * be NULL. The mode parameter can be one of the following:
4356 * FORK_FG - Fork off a foreground process.
4357 * FORK_BG - Fork off a background process.
4358 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4359 * process group even if job control is on.
4360 *
4361 * When job control is turned off, background processes have their standard
4362 * input redirected to /dev/null (except for the second and later processes
4363 * in a pipeline).
4364 *
4365 * Called with interrupts off.
4366 */
4367/*
4368 * Clear traps on a fork.
4369 */
4370static void
4371clear_traps(void)
4372{
4373 char **tp;
4374
4375 for (tp = trap; tp < &trap[NSIG]; tp++) {
4376 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
4377 INT_OFF;
4378 free(*tp);
4379 *tp = NULL;
4380 if (tp != &trap[0])
4381 setsignal(tp - trap);
4382 INT_ON;
4383 }
4384 }
4385}
4386/* lives far away from here, needed for forkchild */
4387static void closescript(void);
4388static void
4389forkchild(struct job *jp, union node *n, int mode)
4390{
4391 int oldlvl;
4392
4393 TRACE(("Child shell %d\n", getpid()));
4394 oldlvl = shlvl;
4395 shlvl++;
4396
4397 closescript();
4398 clear_traps();
4399#if JOBS
4400 /* do job control only in root shell */
4401 jobctl = 0;
4402 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4403 pid_t pgrp;
4404
4405 if (jp->nprocs == 0)
4406 pgrp = getpid();
4407 else
4408 pgrp = jp->ps[0].pid;
4409 /* This can fail because we are doing it in the parent also */
4410 (void)setpgid(0, pgrp);
4411 if (mode == FORK_FG)
4412 xtcsetpgrp(ttyfd, pgrp);
4413 setsignal(SIGTSTP);
4414 setsignal(SIGTTOU);
4415 } else
4416#endif
4417 if (mode == FORK_BG) {
4418 ignoresig(SIGINT);
4419 ignoresig(SIGQUIT);
4420 if (jp->nprocs == 0) {
4421 close(0);
4422 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004423 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004424 }
4425 }
4426 if (!oldlvl && iflag) {
4427 setsignal(SIGINT);
4428 setsignal(SIGQUIT);
4429 setsignal(SIGTERM);
4430 }
4431 for (jp = curjob; jp; jp = jp->prev_job)
4432 freejob(jp);
4433 jobless = 0;
4434}
4435
4436static void
4437forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4438{
4439 TRACE(("In parent shell: child = %d\n", pid));
4440 if (!jp) {
4441 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
4442 jobless++;
4443 return;
4444 }
4445#if JOBS
4446 if (mode != FORK_NOJOB && jp->jobctl) {
4447 int pgrp;
4448
4449 if (jp->nprocs == 0)
4450 pgrp = pid;
4451 else
4452 pgrp = jp->ps[0].pid;
4453 /* This can fail because we are doing it in the child also */
4454 setpgid(pid, pgrp);
4455 }
4456#endif
4457 if (mode == FORK_BG) {
4458 backgndpid = pid; /* set $! */
4459 set_curjob(jp, CUR_RUNNING);
4460 }
4461 if (jp) {
4462 struct procstat *ps = &jp->ps[jp->nprocs++];
4463 ps->pid = pid;
4464 ps->status = -1;
4465 ps->cmd = nullstr;
4466#if JOBS
4467 if (jobctl && n)
4468 ps->cmd = commandtext(n);
4469#endif
4470 }
4471}
4472
4473static int
4474forkshell(struct job *jp, union node *n, int mode)
4475{
4476 int pid;
4477
4478 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4479 pid = fork();
4480 if (pid < 0) {
4481 TRACE(("Fork failed, errno=%d", errno));
4482 if (jp)
4483 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004484 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485 }
4486 if (pid == 0)
4487 forkchild(jp, n, mode);
4488 else
4489 forkparent(jp, n, mode, pid);
4490 return pid;
4491}
4492
4493/*
4494 * Wait for job to finish.
4495 *
4496 * Under job control we have the problem that while a child process is
4497 * running interrupts generated by the user are sent to the child but not
4498 * to the shell. This means that an infinite loop started by an inter-
4499 * active user may be hard to kill. With job control turned off, an
4500 * interactive user may place an interactive program inside a loop. If
4501 * the interactive program catches interrupts, the user doesn't want
4502 * these interrupts to also abort the loop. The approach we take here
4503 * is to have the shell ignore interrupt signals while waiting for a
4504 * foreground process to terminate, and then send itself an interrupt
4505 * signal if the child process was terminated by an interrupt signal.
4506 * Unfortunately, some programs want to do a bit of cleanup and then
4507 * exit on interrupt; unless these processes terminate themselves by
4508 * sending a signal to themselves (instead of calling exit) they will
4509 * confuse this approach.
4510 *
4511 * Called with interrupts off.
4512 */
4513static int
4514waitforjob(struct job *jp)
4515{
4516 int st;
4517
4518 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4519 while (jp->state == JOBRUNNING) {
4520 dowait(DOWAIT_BLOCK, jp);
4521 }
4522 st = getstatus(jp);
4523#if JOBS
4524 if (jp->jobctl) {
4525 xtcsetpgrp(ttyfd, rootpid);
4526 /*
4527 * This is truly gross.
4528 * If we're doing job control, then we did a TIOCSPGRP which
4529 * caused us (the shell) to no longer be in the controlling
4530 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4531 * intuit from the subprocess exit status whether a SIGINT
4532 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4533 */
4534 if (jp->sigint)
4535 raise(SIGINT);
4536 }
4537 if (jp->state == JOBDONE)
4538#endif
4539 freejob(jp);
4540 return st;
4541}
4542
4543/*
4544 * return 1 if there are stopped jobs, otherwise 0
4545 */
4546static int
4547stoppedjobs(void)
4548{
4549 struct job *jp;
4550 int retval;
4551
4552 retval = 0;
4553 if (job_warning)
4554 goto out;
4555 jp = curjob;
4556 if (jp && jp->state == JOBSTOPPED) {
4557 out2str("You have stopped jobs.\n");
4558 job_warning = 2;
4559 retval++;
4560 }
4561 out:
4562 return retval;
4563}
4564
4565
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004566/* ============ redir.c
4567 *
4568 * Code for dealing with input/output redirection.
4569 */
4570
4571#define EMPTY -2 /* marks an unused slot in redirtab */
4572#ifndef PIPE_BUF
4573# define PIPESIZE 4096 /* amount of buffering in a pipe */
4574#else
4575# define PIPESIZE PIPE_BUF
4576#endif
4577
4578/*
4579 * Open a file in noclobber mode.
4580 * The code was copied from bash.
4581 */
4582static int
4583noclobberopen(const char *fname)
4584{
4585 int r, fd;
4586 struct stat finfo, finfo2;
4587
4588 /*
4589 * If the file exists and is a regular file, return an error
4590 * immediately.
4591 */
4592 r = stat(fname, &finfo);
4593 if (r == 0 && S_ISREG(finfo.st_mode)) {
4594 errno = EEXIST;
4595 return -1;
4596 }
4597
4598 /*
4599 * If the file was not present (r != 0), make sure we open it
4600 * exclusively so that if it is created before we open it, our open
4601 * will fail. Make sure that we do not truncate an existing file.
4602 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4603 * file was not a regular file, we leave O_EXCL off.
4604 */
4605 if (r != 0)
4606 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4607 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4608
4609 /* If the open failed, return the file descriptor right away. */
4610 if (fd < 0)
4611 return fd;
4612
4613 /*
4614 * OK, the open succeeded, but the file may have been changed from a
4615 * non-regular file to a regular file between the stat and the open.
4616 * We are assuming that the O_EXCL open handles the case where FILENAME
4617 * did not exist and is symlinked to an existing file between the stat
4618 * and open.
4619 */
4620
4621 /*
4622 * If we can open it and fstat the file descriptor, and neither check
4623 * revealed that it was a regular file, and the file has not been
4624 * replaced, return the file descriptor.
4625 */
4626 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4627 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4628 return fd;
4629
4630 /* The file has been replaced. badness. */
4631 close(fd);
4632 errno = EEXIST;
4633 return -1;
4634}
4635
4636/*
4637 * Handle here documents. Normally we fork off a process to write the
4638 * data to a pipe. If the document is short, we can stuff the data in
4639 * the pipe without forking.
4640 */
4641/* openhere needs this forward reference */
4642static void expandhere(union node *arg, int fd);
4643static int
4644openhere(union node *redir)
4645{
4646 int pip[2];
4647 size_t len = 0;
4648
4649 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004650 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004651 if (redir->type == NHERE) {
4652 len = strlen(redir->nhere.doc->narg.text);
4653 if (len <= PIPESIZE) {
4654 full_write(pip[1], redir->nhere.doc->narg.text, len);
4655 goto out;
4656 }
4657 }
4658 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4659 close(pip[0]);
4660 signal(SIGINT, SIG_IGN);
4661 signal(SIGQUIT, SIG_IGN);
4662 signal(SIGHUP, SIG_IGN);
4663#ifdef SIGTSTP
4664 signal(SIGTSTP, SIG_IGN);
4665#endif
4666 signal(SIGPIPE, SIG_DFL);
4667 if (redir->type == NHERE)
4668 full_write(pip[1], redir->nhere.doc->narg.text, len);
4669 else
4670 expandhere(redir->nhere.doc, pip[1]);
4671 _exit(0);
4672 }
4673 out:
4674 close(pip[1]);
4675 return pip[0];
4676}
4677
4678static int
4679openredirect(union node *redir)
4680{
4681 char *fname;
4682 int f;
4683
4684 switch (redir->nfile.type) {
4685 case NFROM:
4686 fname = redir->nfile.expfname;
4687 f = open(fname, O_RDONLY);
4688 if (f < 0)
4689 goto eopen;
4690 break;
4691 case NFROMTO:
4692 fname = redir->nfile.expfname;
4693 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4694 if (f < 0)
4695 goto ecreate;
4696 break;
4697 case NTO:
4698 /* Take care of noclobber mode. */
4699 if (Cflag) {
4700 fname = redir->nfile.expfname;
4701 f = noclobberopen(fname);
4702 if (f < 0)
4703 goto ecreate;
4704 break;
4705 }
4706 /* FALLTHROUGH */
4707 case NCLOBBER:
4708 fname = redir->nfile.expfname;
4709 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4710 if (f < 0)
4711 goto ecreate;
4712 break;
4713 case NAPPEND:
4714 fname = redir->nfile.expfname;
4715 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4716 if (f < 0)
4717 goto ecreate;
4718 break;
4719 default:
4720#if DEBUG
4721 abort();
4722#endif
4723 /* Fall through to eliminate warning. */
4724 case NTOFD:
4725 case NFROMFD:
4726 f = -1;
4727 break;
4728 case NHERE:
4729 case NXHERE:
4730 f = openhere(redir);
4731 break;
4732 }
4733
4734 return f;
4735 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004736 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004737 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004738 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004739}
4740
4741/*
4742 * Copy a file descriptor to be >= to. Returns -1
4743 * if the source file descriptor is closed, EMPTY if there are no unused
4744 * file descriptors left.
4745 */
4746static int
4747copyfd(int from, int to)
4748{
4749 int newfd;
4750
4751 newfd = fcntl(from, F_DUPFD, to);
4752 if (newfd < 0) {
4753 if (errno == EMFILE)
4754 return EMPTY;
4755 ash_msg_and_raise_error("%d: %m", from);
4756 }
4757 return newfd;
4758}
4759
4760static void
4761dupredirect(union node *redir, int f)
4762{
4763 int fd = redir->nfile.fd;
4764
4765 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4766 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4767 copyfd(redir->ndup.dupfd, fd);
4768 }
4769 return;
4770 }
4771
4772 if (f != fd) {
4773 copyfd(f, fd);
4774 close(f);
4775 }
4776}
4777
4778/*
4779 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4780 * old file descriptors are stashed away so that the redirection can be
4781 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4782 * standard output, and the standard error if it becomes a duplicate of
4783 * stdout, is saved in memory.
4784 */
4785/* flags passed to redirect */
4786#define REDIR_PUSH 01 /* save previous values of file descriptors */
4787#define REDIR_SAVEFD2 03 /* set preverrout */
4788static void
4789redirect(union node *redir, int flags)
4790{
4791 union node *n;
4792 struct redirtab *sv;
4793 int i;
4794 int fd;
4795 int newfd;
4796 int *p;
4797 nullredirs++;
4798 if (!redir) {
4799 return;
4800 }
4801 sv = NULL;
4802 INT_OFF;
4803 if (flags & REDIR_PUSH) {
4804 struct redirtab *q;
4805 q = ckmalloc(sizeof(struct redirtab));
4806 q->next = redirlist;
4807 redirlist = q;
4808 q->nullredirs = nullredirs - 1;
4809 for (i = 0; i < 10; i++)
4810 q->renamed[i] = EMPTY;
4811 nullredirs = 0;
4812 sv = q;
4813 }
4814 n = redir;
4815 do {
4816 fd = n->nfile.fd;
4817 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4818 && n->ndup.dupfd == fd)
4819 continue; /* redirect from/to same file descriptor */
4820
4821 newfd = openredirect(n);
4822 if (fd == newfd)
4823 continue;
4824 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
4825 i = fcntl(fd, F_DUPFD, 10);
4826
4827 if (i == -1) {
4828 i = errno;
4829 if (i != EBADF) {
4830 close(newfd);
4831 errno = i;
4832 ash_msg_and_raise_error("%d: %m", fd);
4833 /* NOTREACHED */
4834 }
4835 } else {
4836 *p = i;
4837 close(fd);
4838 }
4839 } else {
4840 close(fd);
4841 }
4842 dupredirect(n, newfd);
4843 } while ((n = n->nfile.next));
4844 INT_ON;
4845 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
4846 preverrout_fd = sv->renamed[2];
4847}
4848
4849/*
4850 * Undo the effects of the last redirection.
4851 */
4852static void
4853popredir(int drop)
4854{
4855 struct redirtab *rp;
4856 int i;
4857
4858 if (--nullredirs >= 0)
4859 return;
4860 INT_OFF;
4861 rp = redirlist;
4862 for (i = 0; i < 10; i++) {
4863 if (rp->renamed[i] != EMPTY) {
4864 if (!drop) {
4865 close(i);
4866 copyfd(rp->renamed[i], i);
4867 }
4868 close(rp->renamed[i]);
4869 }
4870 }
4871 redirlist = rp->next;
4872 nullredirs = rp->nullredirs;
4873 free(rp);
4874 INT_ON;
4875}
4876
4877/*
4878 * Undo all redirections. Called on error or interrupt.
4879 */
4880
4881/*
4882 * Discard all saved file descriptors.
4883 */
4884static void
4885clearredir(int drop)
4886{
4887 for (;;) {
4888 nullredirs = 0;
4889 if (!redirlist)
4890 break;
4891 popredir(drop);
4892 }
4893}
4894
4895static int
4896redirectsafe(union node *redir, int flags)
4897{
4898 int err;
4899 volatile int saveint;
4900 struct jmploc *volatile savehandler = exception_handler;
4901 struct jmploc jmploc;
4902
4903 SAVE_INT(saveint);
4904 err = setjmp(jmploc.loc) * 2;
4905 if (!err) {
4906 exception_handler = &jmploc;
4907 redirect(redir, flags);
4908 }
4909 exception_handler = savehandler;
4910 if (err && exception != EXERROR)
4911 longjmp(exception_handler->loc, 1);
4912 RESTORE_INT(saveint);
4913 return err;
4914}
4915
4916
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00004917/* ============ Routines to expand arguments to commands
4918 *
4919 * We have to deal with backquotes, shell variables, and file metacharacters.
4920 */
4921
4922/*
4923 * expandarg flags
4924 */
4925#define EXP_FULL 0x1 /* perform word splitting & file globbing */
4926#define EXP_TILDE 0x2 /* do normal tilde expansion */
4927#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
4928#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
4929#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
4930#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
4931#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
4932#define EXP_WORD 0x80 /* expand word in parameter expansion */
4933#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
4934/*
4935 * _rmescape() flags
4936 */
4937#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4938#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4939#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4940#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4941#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4942
4943/*
4944 * Structure specifying which parts of the string should be searched
4945 * for IFS characters.
4946 */
4947struct ifsregion {
4948 struct ifsregion *next; /* next region in list */
4949 int begoff; /* offset of start of region */
4950 int endoff; /* offset of end of region */
4951 int nulonly; /* search for nul bytes only */
4952};
4953
4954struct arglist {
4955 struct strlist *list;
4956 struct strlist **lastp;
4957};
4958
4959/* output of current string */
4960static char *expdest;
4961/* list of back quote expressions */
4962static struct nodelist *argbackq;
4963/* first struct in list of ifs regions */
4964static struct ifsregion ifsfirst;
4965/* last struct in list */
4966static struct ifsregion *ifslastp;
4967/* holds expanded arg list */
4968static struct arglist exparg;
4969
4970/*
4971 * Our own itoa().
4972 */
4973static int
4974cvtnum(arith_t num)
4975{
4976 int len;
4977
4978 expdest = makestrspace(32, expdest);
4979#if ENABLE_ASH_MATH_SUPPORT_64
4980 len = fmtstr(expdest, 32, "%lld", (long long) num);
4981#else
4982 len = fmtstr(expdest, 32, "%ld", num);
4983#endif
4984 STADJUST(len, expdest);
4985 return len;
4986}
4987
4988static size_t
4989esclen(const char *start, const char *p)
4990{
4991 size_t esc = 0;
4992
4993 while (p > start && *--p == CTLESC) {
4994 esc++;
4995 }
4996 return esc;
4997}
4998
4999/*
5000 * Remove any CTLESC characters from a string.
5001 */
5002static char *
5003_rmescapes(char *str, int flag)
5004{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005005 static const char qchars[] = { CTLESC, CTLQUOTEMARK, '\0' };
5006
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005007 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005008 unsigned inquotes;
5009 int notescaped;
5010 int globbing;
5011
5012 p = strpbrk(str, qchars);
5013 if (!p) {
5014 return str;
5015 }
5016 q = p;
5017 r = str;
5018 if (flag & RMESCAPE_ALLOC) {
5019 size_t len = p - str;
5020 size_t fulllen = len + strlen(p) + 1;
5021
5022 if (flag & RMESCAPE_GROW) {
5023 r = makestrspace(fulllen, expdest);
5024 } else if (flag & RMESCAPE_HEAP) {
5025 r = ckmalloc(fulllen);
5026 } else {
5027 r = stalloc(fulllen);
5028 }
5029 q = r;
5030 if (len > 0) {
5031 q = memcpy(q, str, len) + len;
5032 }
5033 }
5034 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5035 globbing = flag & RMESCAPE_GLOB;
5036 notescaped = globbing;
5037 while (*p) {
5038 if (*p == CTLQUOTEMARK) {
5039 inquotes = ~inquotes;
5040 p++;
5041 notescaped = globbing;
5042 continue;
5043 }
5044 if (*p == '\\') {
5045 /* naked back slash */
5046 notescaped = 0;
5047 goto copy;
5048 }
5049 if (*p == CTLESC) {
5050 p++;
5051 if (notescaped && inquotes && *p != '/') {
5052 *q++ = '\\';
5053 }
5054 }
5055 notescaped = globbing;
5056 copy:
5057 *q++ = *p++;
5058 }
5059 *q = '\0';
5060 if (flag & RMESCAPE_GROW) {
5061 expdest = r;
5062 STADJUST(q - r + 1, expdest);
5063 }
5064 return r;
5065}
5066#define rmescapes(p) _rmescapes((p), 0)
5067
5068#define pmatch(a, b) !fnmatch((a), (b), 0)
5069
5070/*
5071 * Prepare a pattern for a expmeta (internal glob(3)) call.
5072 *
5073 * Returns an stalloced string.
5074 */
5075static char *
5076preglob(const char *pattern, int quoted, int flag)
5077{
5078 flag |= RMESCAPE_GLOB;
5079 if (quoted) {
5080 flag |= RMESCAPE_QUOTED;
5081 }
5082 return _rmescapes((char *)pattern, flag);
5083}
5084
5085/*
5086 * Put a string on the stack.
5087 */
5088static void
5089memtodest(const char *p, size_t len, int syntax, int quotes)
5090{
5091 char *q = expdest;
5092
5093 q = makestrspace(len * 2, q);
5094
5095 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005096 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005097 if (!c)
5098 continue;
5099 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5100 USTPUTC(CTLESC, q);
5101 USTPUTC(c, q);
5102 }
5103
5104 expdest = q;
5105}
5106
5107static void
5108strtodest(const char *p, int syntax, int quotes)
5109{
5110 memtodest(p, strlen(p), syntax, quotes);
5111}
5112
5113/*
5114 * Record the fact that we have to scan this region of the
5115 * string for IFS characters.
5116 */
5117static void
5118recordregion(int start, int end, int nulonly)
5119{
5120 struct ifsregion *ifsp;
5121
5122 if (ifslastp == NULL) {
5123 ifsp = &ifsfirst;
5124 } else {
5125 INT_OFF;
5126 ifsp = ckmalloc(sizeof(*ifsp));
5127 ifsp->next = NULL;
5128 ifslastp->next = ifsp;
5129 INT_ON;
5130 }
5131 ifslastp = ifsp;
5132 ifslastp->begoff = start;
5133 ifslastp->endoff = end;
5134 ifslastp->nulonly = nulonly;
5135}
5136
5137static void
5138removerecordregions(int endoff)
5139{
5140 if (ifslastp == NULL)
5141 return;
5142
5143 if (ifsfirst.endoff > endoff) {
5144 while (ifsfirst.next != NULL) {
5145 struct ifsregion *ifsp;
5146 INT_OFF;
5147 ifsp = ifsfirst.next->next;
5148 free(ifsfirst.next);
5149 ifsfirst.next = ifsp;
5150 INT_ON;
5151 }
5152 if (ifsfirst.begoff > endoff)
5153 ifslastp = NULL;
5154 else {
5155 ifslastp = &ifsfirst;
5156 ifsfirst.endoff = endoff;
5157 }
5158 return;
5159 }
5160
5161 ifslastp = &ifsfirst;
5162 while (ifslastp->next && ifslastp->next->begoff < endoff)
5163 ifslastp=ifslastp->next;
5164 while (ifslastp->next != NULL) {
5165 struct ifsregion *ifsp;
5166 INT_OFF;
5167 ifsp = ifslastp->next->next;
5168 free(ifslastp->next);
5169 ifslastp->next = ifsp;
5170 INT_ON;
5171 }
5172 if (ifslastp->endoff > endoff)
5173 ifslastp->endoff = endoff;
5174}
5175
5176static char *
5177exptilde(char *startp, char *p, int flag)
5178{
5179 char c;
5180 char *name;
5181 struct passwd *pw;
5182 const char *home;
5183 int quotes = flag & (EXP_FULL | EXP_CASE);
5184 int startloc;
5185
5186 name = p + 1;
5187
5188 while ((c = *++p) != '\0') {
5189 switch (c) {
5190 case CTLESC:
5191 return startp;
5192 case CTLQUOTEMARK:
5193 return startp;
5194 case ':':
5195 if (flag & EXP_VARTILDE)
5196 goto done;
5197 break;
5198 case '/':
5199 case CTLENDVAR:
5200 goto done;
5201 }
5202 }
5203 done:
5204 *p = '\0';
5205 if (*name == '\0') {
5206 home = lookupvar(homestr);
5207 } else {
5208 pw = getpwnam(name);
5209 if (pw == NULL)
5210 goto lose;
5211 home = pw->pw_dir;
5212 }
5213 if (!home || !*home)
5214 goto lose;
5215 *p = c;
5216 startloc = expdest - (char *)stackblock();
5217 strtodest(home, SQSYNTAX, quotes);
5218 recordregion(startloc, expdest - (char *)stackblock(), 0);
5219 return p;
5220 lose:
5221 *p = c;
5222 return startp;
5223}
5224
5225/*
5226 * Execute a command inside back quotes. If it's a builtin command, we
5227 * want to save its output in a block obtained from malloc. Otherwise
5228 * we fork off a subprocess and get the output of the command via a pipe.
5229 * Should be called with interrupts off.
5230 */
5231struct backcmd { /* result of evalbackcmd */
5232 int fd; /* file descriptor to read from */
5233 char *buf; /* buffer */
5234 int nleft; /* number of chars in buffer */
5235 struct job *jp; /* job structure for command */
5236};
5237
5238/* These forward decls are needed to use "eval" code for backticks handling: */
5239static int back_exitstatus; /* exit status of backquoted command */
5240#define EV_EXIT 01 /* exit after evaluating tree */
5241static void evaltree(union node *, int);
5242
5243static void
5244evalbackcmd(union node *n, struct backcmd *result)
5245{
5246 int saveherefd;
5247
5248 result->fd = -1;
5249 result->buf = NULL;
5250 result->nleft = 0;
5251 result->jp = NULL;
5252 if (n == NULL) {
5253 goto out;
5254 }
5255
5256 saveherefd = herefd;
5257 herefd = -1;
5258
5259 {
5260 int pip[2];
5261 struct job *jp;
5262
5263 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005264 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005265 jp = makejob(n, 1);
5266 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5267 FORCE_INT_ON;
5268 close(pip[0]);
5269 if (pip[1] != 1) {
5270 close(1);
5271 copyfd(pip[1], 1);
5272 close(pip[1]);
5273 }
5274 eflag = 0;
5275 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5276 /* NOTREACHED */
5277 }
5278 close(pip[1]);
5279 result->fd = pip[0];
5280 result->jp = jp;
5281 }
5282 herefd = saveherefd;
5283 out:
5284 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5285 result->fd, result->buf, result->nleft, result->jp));
5286}
5287
5288/*
5289 * Expand stuff in backwards quotes.
5290 */
5291static void
5292expbackq(union node *cmd, int quoted, int quotes)
5293{
5294 struct backcmd in;
5295 int i;
5296 char buf[128];
5297 char *p;
5298 char *dest;
5299 int startloc;
5300 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5301 struct stackmark smark;
5302
5303 INT_OFF;
5304 setstackmark(&smark);
5305 dest = expdest;
5306 startloc = dest - (char *)stackblock();
5307 grabstackstr(dest);
5308 evalbackcmd(cmd, &in);
5309 popstackmark(&smark);
5310
5311 p = in.buf;
5312 i = in.nleft;
5313 if (i == 0)
5314 goto read;
5315 for (;;) {
5316 memtodest(p, i, syntax, quotes);
5317 read:
5318 if (in.fd < 0)
5319 break;
5320 i = safe_read(in.fd, buf, sizeof(buf));
5321 TRACE(("expbackq: read returns %d\n", i));
5322 if (i <= 0)
5323 break;
5324 p = buf;
5325 }
5326
5327 if (in.buf)
5328 free(in.buf);
5329 if (in.fd >= 0) {
5330 close(in.fd);
5331 back_exitstatus = waitforjob(in.jp);
5332 }
5333 INT_ON;
5334
5335 /* Eat all trailing newlines */
5336 dest = expdest;
5337 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5338 STUNPUTC(dest);
5339 expdest = dest;
5340
5341 if (quoted == 0)
5342 recordregion(startloc, dest - (char *)stackblock(), 0);
5343 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5344 (dest - (char *)stackblock()) - startloc,
5345 (dest - (char *)stackblock()) - startloc,
5346 stackblock() + startloc));
5347}
5348
5349#if ENABLE_ASH_MATH_SUPPORT
5350/*
5351 * Expand arithmetic expression. Backup to start of expression,
5352 * evaluate, place result in (backed up) result, adjust string position.
5353 */
5354static void
5355expari(int quotes)
5356{
5357 char *p, *start;
5358 int begoff;
5359 int flag;
5360 int len;
5361
5362 /* ifsfree(); */
5363
5364 /*
5365 * This routine is slightly over-complicated for
5366 * efficiency. Next we scan backwards looking for the
5367 * start of arithmetic.
5368 */
5369 start = stackblock();
5370 p = expdest - 1;
5371 *p = '\0';
5372 p--;
5373 do {
5374 int esc;
5375
5376 while (*p != CTLARI) {
5377 p--;
5378#if DEBUG
5379 if (p < start) {
5380 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5381 }
5382#endif
5383 }
5384
5385 esc = esclen(start, p);
5386 if (!(esc % 2)) {
5387 break;
5388 }
5389
5390 p -= esc + 1;
5391 } while (1);
5392
5393 begoff = p - start;
5394
5395 removerecordregions(begoff);
5396
5397 flag = p[1];
5398
5399 expdest = p;
5400
5401 if (quotes)
5402 rmescapes(p + 2);
5403
5404 len = cvtnum(dash_arith(p + 2));
5405
5406 if (flag != '"')
5407 recordregion(begoff, begoff + len, 0);
5408}
5409#endif
5410
5411/* argstr needs it */
5412static char *evalvar(char *p, int flag);
5413
5414/*
5415 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5416 * characters to allow for further processing. Otherwise treat
5417 * $@ like $* since no splitting will be performed.
5418 */
5419static void
5420argstr(char *p, int flag)
5421{
5422 static const char spclchars[] = {
5423 '=',
5424 ':',
5425 CTLQUOTEMARK,
5426 CTLENDVAR,
5427 CTLESC,
5428 CTLVAR,
5429 CTLBACKQ,
5430 CTLBACKQ | CTLQUOTE,
5431#if ENABLE_ASH_MATH_SUPPORT
5432 CTLENDARI,
5433#endif
5434 0
5435 };
5436 const char *reject = spclchars;
5437 int c;
5438 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5439 int breakall = flag & EXP_WORD;
5440 int inquotes;
5441 size_t length;
5442 int startloc;
5443
5444 if (!(flag & EXP_VARTILDE)) {
5445 reject += 2;
5446 } else if (flag & EXP_VARTILDE2) {
5447 reject++;
5448 }
5449 inquotes = 0;
5450 length = 0;
5451 if (flag & EXP_TILDE) {
5452 char *q;
5453
5454 flag &= ~EXP_TILDE;
5455 tilde:
5456 q = p;
5457 if (*q == CTLESC && (flag & EXP_QWORD))
5458 q++;
5459 if (*q == '~')
5460 p = exptilde(p, q, flag);
5461 }
5462 start:
5463 startloc = expdest - (char *)stackblock();
5464 for (;;) {
5465 length += strcspn(p + length, reject);
5466 c = p[length];
5467 if (c && (!(c & 0x80)
5468#if ENABLE_ASH_MATH_SUPPORT
5469 || c == CTLENDARI
5470#endif
5471 )) {
5472 /* c == '=' || c == ':' || c == CTLENDARI */
5473 length++;
5474 }
5475 if (length > 0) {
5476 int newloc;
5477 expdest = stack_nputstr(p, length, expdest);
5478 newloc = expdest - (char *)stackblock();
5479 if (breakall && !inquotes && newloc > startloc) {
5480 recordregion(startloc, newloc, 0);
5481 }
5482 startloc = newloc;
5483 }
5484 p += length + 1;
5485 length = 0;
5486
5487 switch (c) {
5488 case '\0':
5489 goto breakloop;
5490 case '=':
5491 if (flag & EXP_VARTILDE2) {
5492 p--;
5493 continue;
5494 }
5495 flag |= EXP_VARTILDE2;
5496 reject++;
5497 /* fall through */
5498 case ':':
5499 /*
5500 * sort of a hack - expand tildes in variable
5501 * assignments (after the first '=' and after ':'s).
5502 */
5503 if (*--p == '~') {
5504 goto tilde;
5505 }
5506 continue;
5507 }
5508
5509 switch (c) {
5510 case CTLENDVAR: /* ??? */
5511 goto breakloop;
5512 case CTLQUOTEMARK:
5513 /* "$@" syntax adherence hack */
5514 if (
5515 !inquotes &&
5516 !memcmp(p, dolatstr, 4) &&
5517 (p[4] == CTLQUOTEMARK || (
5518 p[4] == CTLENDVAR &&
5519 p[5] == CTLQUOTEMARK
5520 ))
5521 ) {
5522 p = evalvar(p + 1, flag) + 1;
5523 goto start;
5524 }
5525 inquotes = !inquotes;
5526 addquote:
5527 if (quotes) {
5528 p--;
5529 length++;
5530 startloc++;
5531 }
5532 break;
5533 case CTLESC:
5534 startloc++;
5535 length++;
5536 goto addquote;
5537 case CTLVAR:
5538 p = evalvar(p, flag);
5539 goto start;
5540 case CTLBACKQ:
5541 c = 0;
5542 case CTLBACKQ|CTLQUOTE:
5543 expbackq(argbackq->n, c, quotes);
5544 argbackq = argbackq->next;
5545 goto start;
5546#if ENABLE_ASH_MATH_SUPPORT
5547 case CTLENDARI:
5548 p--;
5549 expari(quotes);
5550 goto start;
5551#endif
5552 }
5553 }
5554 breakloop:
5555 ;
5556}
5557
5558static char *
5559scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5560 int zero)
5561{
5562 char *loc;
5563 char *loc2;
5564 char c;
5565
5566 loc = startp;
5567 loc2 = rmesc;
5568 do {
5569 int match;
5570 const char *s = loc2;
5571 c = *loc2;
5572 if (zero) {
5573 *loc2 = '\0';
5574 s = rmesc;
5575 }
5576 match = pmatch(str, s);
5577 *loc2 = c;
5578 if (match)
5579 return loc;
5580 if (quotes && *loc == CTLESC)
5581 loc++;
5582 loc++;
5583 loc2++;
5584 } while (c);
5585 return 0;
5586}
5587
5588static char *
5589scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5590 int zero)
5591{
5592 int esc = 0;
5593 char *loc;
5594 char *loc2;
5595
5596 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5597 int match;
5598 char c = *loc2;
5599 const char *s = loc2;
5600 if (zero) {
5601 *loc2 = '\0';
5602 s = rmesc;
5603 }
5604 match = pmatch(str, s);
5605 *loc2 = c;
5606 if (match)
5607 return loc;
5608 loc--;
5609 if (quotes) {
5610 if (--esc < 0) {
5611 esc = esclen(startp, loc);
5612 }
5613 if (esc % 2) {
5614 esc--;
5615 loc--;
5616 }
5617 }
5618 }
5619 return 0;
5620}
5621
5622static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5623static void
5624varunset(const char *end, const char *var, const char *umsg, int varflags)
5625{
5626 const char *msg;
5627 const char *tail;
5628
5629 tail = nullstr;
5630 msg = "parameter not set";
5631 if (umsg) {
5632 if (*end == CTLENDVAR) {
5633 if (varflags & VSNUL)
5634 tail = " or null";
5635 } else
5636 msg = umsg;
5637 }
5638 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5639}
5640
5641static const char *
5642subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5643{
5644 char *startp;
5645 char *loc;
5646 int saveherefd = herefd;
5647 struct nodelist *saveargbackq = argbackq;
5648 int amount;
5649 char *rmesc, *rmescend;
5650 int zero;
5651 char *(*scan)(char *, char *, char *, char *, int , int);
5652
5653 herefd = -1;
5654 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5655 STPUTC('\0', expdest);
5656 herefd = saveherefd;
5657 argbackq = saveargbackq;
5658 startp = stackblock() + startloc;
5659
5660 switch (subtype) {
5661 case VSASSIGN:
5662 setvar(str, startp, 0);
5663 amount = startp - expdest;
5664 STADJUST(amount, expdest);
5665 return startp;
5666
5667 case VSQUESTION:
5668 varunset(p, str, startp, varflags);
5669 /* NOTREACHED */
5670 }
5671
5672 subtype -= VSTRIMRIGHT;
5673#if DEBUG
5674 if (subtype < 0 || subtype > 3)
5675 abort();
5676#endif
5677
5678 rmesc = startp;
5679 rmescend = stackblock() + strloc;
5680 if (quotes) {
5681 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5682 if (rmesc != startp) {
5683 rmescend = expdest;
5684 startp = stackblock() + startloc;
5685 }
5686 }
5687 rmescend--;
5688 str = stackblock() + strloc;
5689 preglob(str, varflags & VSQUOTE, 0);
5690
5691 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5692 zero = subtype >> 1;
5693 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5694 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5695
5696 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5697 if (loc) {
5698 if (zero) {
5699 memmove(startp, loc, str - loc);
5700 loc = startp + (str - loc) - 1;
5701 }
5702 *loc = '\0';
5703 amount = loc - expdest;
5704 STADJUST(amount, expdest);
5705 }
5706 return loc;
5707}
5708
5709/*
5710 * Add the value of a specialized variable to the stack string.
5711 */
5712static ssize_t
5713varvalue(char *name, int varflags, int flags)
5714{
5715 int num;
5716 char *p;
5717 int i;
5718 int sep = 0;
5719 int sepq = 0;
5720 ssize_t len = 0;
5721 char **ap;
5722 int syntax;
5723 int quoted = varflags & VSQUOTE;
5724 int subtype = varflags & VSTYPE;
5725 int quotes = flags & (EXP_FULL | EXP_CASE);
5726
5727 if (quoted && (flags & EXP_FULL))
5728 sep = 1 << CHAR_BIT;
5729
5730 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5731 switch (*name) {
5732 case '$':
5733 num = rootpid;
5734 goto numvar;
5735 case '?':
5736 num = exitstatus;
5737 goto numvar;
5738 case '#':
5739 num = shellparam.nparam;
5740 goto numvar;
5741 case '!':
5742 num = backgndpid;
5743 if (num == 0)
5744 return -1;
5745 numvar:
5746 len = cvtnum(num);
5747 break;
5748 case '-':
5749 p = makestrspace(NOPTS, expdest);
5750 for (i = NOPTS - 1; i >= 0; i--) {
5751 if (optlist[i]) {
5752 USTPUTC(optletters(i), p);
5753 len++;
5754 }
5755 }
5756 expdest = p;
5757 break;
5758 case '@':
5759 if (sep)
5760 goto param;
5761 /* fall through */
5762 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005763 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005764 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5765 sepq = 1;
5766 param:
5767 ap = shellparam.p;
5768 if (!ap)
5769 return -1;
5770 while ((p = *ap++)) {
5771 size_t partlen;
5772
5773 partlen = strlen(p);
5774 len += partlen;
5775
5776 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5777 memtodest(p, partlen, syntax, quotes);
5778
5779 if (*ap && sep) {
5780 char *q;
5781
5782 len++;
5783 if (subtype == VSPLUS || subtype == VSLENGTH) {
5784 continue;
5785 }
5786 q = expdest;
5787 if (sepq)
5788 STPUTC(CTLESC, q);
5789 STPUTC(sep, q);
5790 expdest = q;
5791 }
5792 }
5793 return len;
5794 case '0':
5795 case '1':
5796 case '2':
5797 case '3':
5798 case '4':
5799 case '5':
5800 case '6':
5801 case '7':
5802 case '8':
5803 case '9':
5804 num = atoi(name);
5805 if (num < 0 || num > shellparam.nparam)
5806 return -1;
5807 p = num ? shellparam.p[num - 1] : arg0;
5808 goto value;
5809 default:
5810 p = lookupvar(name);
5811 value:
5812 if (!p)
5813 return -1;
5814
5815 len = strlen(p);
5816 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5817 memtodest(p, len, syntax, quotes);
5818 return len;
5819 }
5820
5821 if (subtype == VSPLUS || subtype == VSLENGTH)
5822 STADJUST(-len, expdest);
5823 return len;
5824}
5825
5826/*
5827 * Expand a variable, and return a pointer to the next character in the
5828 * input string.
5829 */
5830static char *
5831evalvar(char *p, int flag)
5832{
5833 int subtype;
5834 int varflags;
5835 char *var;
5836 int patloc;
5837 int c;
5838 int startloc;
5839 ssize_t varlen;
5840 int easy;
5841 int quotes;
5842 int quoted;
5843
5844 quotes = flag & (EXP_FULL | EXP_CASE);
5845 varflags = *p++;
5846 subtype = varflags & VSTYPE;
5847 quoted = varflags & VSQUOTE;
5848 var = p;
5849 easy = (!quoted || (*var == '@' && shellparam.nparam));
5850 startloc = expdest - (char *)stackblock();
5851 p = strchr(p, '=') + 1;
5852
5853 again:
5854 varlen = varvalue(var, varflags, flag);
5855 if (varflags & VSNUL)
5856 varlen--;
5857
5858 if (subtype == VSPLUS) {
5859 varlen = -1 - varlen;
5860 goto vsplus;
5861 }
5862
5863 if (subtype == VSMINUS) {
5864 vsplus:
5865 if (varlen < 0) {
5866 argstr(
5867 p, flag | EXP_TILDE |
5868 (quoted ? EXP_QWORD : EXP_WORD)
5869 );
5870 goto end;
5871 }
5872 if (easy)
5873 goto record;
5874 goto end;
5875 }
5876
5877 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5878 if (varlen < 0) {
5879 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
5880 varflags &= ~VSNUL;
5881 /*
5882 * Remove any recorded regions beyond
5883 * start of variable
5884 */
5885 removerecordregions(startloc);
5886 goto again;
5887 }
5888 goto end;
5889 }
5890 if (easy)
5891 goto record;
5892 goto end;
5893 }
5894
5895 if (varlen < 0 && uflag)
5896 varunset(p, var, 0, 0);
5897
5898 if (subtype == VSLENGTH) {
5899 cvtnum(varlen > 0 ? varlen : 0);
5900 goto record;
5901 }
5902
5903 if (subtype == VSNORMAL) {
5904 if (!easy)
5905 goto end;
5906 record:
5907 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5908 goto end;
5909 }
5910
5911#if DEBUG
5912 switch (subtype) {
5913 case VSTRIMLEFT:
5914 case VSTRIMLEFTMAX:
5915 case VSTRIMRIGHT:
5916 case VSTRIMRIGHTMAX:
5917 break;
5918 default:
5919 abort();
5920 }
5921#endif
5922
5923 if (varlen >= 0) {
5924 /*
5925 * Terminate the string and start recording the pattern
5926 * right after it
5927 */
5928 STPUTC('\0', expdest);
5929 patloc = expdest - (char *)stackblock();
5930 if (subevalvar(p, NULL, patloc, subtype,
5931 startloc, varflags, quotes) == 0) {
5932 int amount = expdest - (
5933 (char *)stackblock() + patloc - 1
5934 );
5935 STADJUST(-amount, expdest);
5936 }
5937 /* Remove any recorded regions beyond start of variable */
5938 removerecordregions(startloc);
5939 goto record;
5940 }
5941
5942 end:
5943 if (subtype != VSNORMAL) { /* skip to end of alternative */
5944 int nesting = 1;
5945 for (;;) {
5946 c = *p++;
5947 if (c == CTLESC)
5948 p++;
5949 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5950 if (varlen >= 0)
5951 argbackq = argbackq->next;
5952 } else if (c == CTLVAR) {
5953 if ((*p++ & VSTYPE) != VSNORMAL)
5954 nesting++;
5955 } else if (c == CTLENDVAR) {
5956 if (--nesting == 0)
5957 break;
5958 }
5959 }
5960 }
5961 return p;
5962}
5963
5964/*
5965 * Break the argument string into pieces based upon IFS and add the
5966 * strings to the argument list. The regions of the string to be
5967 * searched for IFS characters have been stored by recordregion.
5968 */
5969static void
5970ifsbreakup(char *string, struct arglist *arglist)
5971{
5972 struct ifsregion *ifsp;
5973 struct strlist *sp;
5974 char *start;
5975 char *p;
5976 char *q;
5977 const char *ifs, *realifs;
5978 int ifsspc;
5979 int nulonly;
5980
5981 start = string;
5982 if (ifslastp != NULL) {
5983 ifsspc = 0;
5984 nulonly = 0;
5985 realifs = ifsset() ? ifsval() : defifs;
5986 ifsp = &ifsfirst;
5987 do {
5988 p = string + ifsp->begoff;
5989 nulonly = ifsp->nulonly;
5990 ifs = nulonly ? nullstr : realifs;
5991 ifsspc = 0;
5992 while (p < string + ifsp->endoff) {
5993 q = p;
5994 if (*p == CTLESC)
5995 p++;
5996 if (!strchr(ifs, *p)) {
5997 p++;
5998 continue;
5999 }
6000 if (!nulonly)
6001 ifsspc = (strchr(defifs, *p) != NULL);
6002 /* Ignore IFS whitespace at start */
6003 if (q == start && ifsspc) {
6004 p++;
6005 start = p;
6006 continue;
6007 }
6008 *q = '\0';
6009 sp = stalloc(sizeof(*sp));
6010 sp->text = start;
6011 *arglist->lastp = sp;
6012 arglist->lastp = &sp->next;
6013 p++;
6014 if (!nulonly) {
6015 for (;;) {
6016 if (p >= string + ifsp->endoff) {
6017 break;
6018 }
6019 q = p;
6020 if (*p == CTLESC)
6021 p++;
6022 if (strchr(ifs, *p) == NULL ) {
6023 p = q;
6024 break;
6025 } else if (strchr(defifs, *p) == NULL) {
6026 if (ifsspc) {
6027 p++;
6028 ifsspc = 0;
6029 } else {
6030 p = q;
6031 break;
6032 }
6033 } else
6034 p++;
6035 }
6036 }
6037 start = p;
6038 } /* while */
6039 ifsp = ifsp->next;
6040 } while (ifsp != NULL);
6041 if (nulonly)
6042 goto add;
6043 }
6044
6045 if (!*start)
6046 return;
6047
6048 add:
6049 sp = stalloc(sizeof(*sp));
6050 sp->text = start;
6051 *arglist->lastp = sp;
6052 arglist->lastp = &sp->next;
6053}
6054
6055static void
6056ifsfree(void)
6057{
6058 struct ifsregion *p;
6059
6060 INT_OFF;
6061 p = ifsfirst.next;
6062 do {
6063 struct ifsregion *ifsp;
6064 ifsp = p->next;
6065 free(p);
6066 p = ifsp;
6067 } while (p);
6068 ifslastp = NULL;
6069 ifsfirst.next = NULL;
6070 INT_ON;
6071}
6072
6073/*
6074 * Add a file name to the list.
6075 */
6076static void
6077addfname(const char *name)
6078{
6079 struct strlist *sp;
6080
6081 sp = stalloc(sizeof(*sp));
6082 sp->text = ststrdup(name);
6083 *exparg.lastp = sp;
6084 exparg.lastp = &sp->next;
6085}
6086
6087static char *expdir;
6088
6089/*
6090 * Do metacharacter (i.e. *, ?, [...]) expansion.
6091 */
6092static void
6093expmeta(char *enddir, char *name)
6094{
6095 char *p;
6096 const char *cp;
6097 char *start;
6098 char *endname;
6099 int metaflag;
6100 struct stat statb;
6101 DIR *dirp;
6102 struct dirent *dp;
6103 int atend;
6104 int matchdot;
6105
6106 metaflag = 0;
6107 start = name;
6108 for (p = name; *p; p++) {
6109 if (*p == '*' || *p == '?')
6110 metaflag = 1;
6111 else if (*p == '[') {
6112 char *q = p + 1;
6113 if (*q == '!')
6114 q++;
6115 for (;;) {
6116 if (*q == '\\')
6117 q++;
6118 if (*q == '/' || *q == '\0')
6119 break;
6120 if (*++q == ']') {
6121 metaflag = 1;
6122 break;
6123 }
6124 }
6125 } else if (*p == '\\')
6126 p++;
6127 else if (*p == '/') {
6128 if (metaflag)
6129 goto out;
6130 start = p + 1;
6131 }
6132 }
6133 out:
6134 if (metaflag == 0) { /* we've reached the end of the file name */
6135 if (enddir != expdir)
6136 metaflag++;
6137 p = name;
6138 do {
6139 if (*p == '\\')
6140 p++;
6141 *enddir++ = *p;
6142 } while (*p++);
6143 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6144 addfname(expdir);
6145 return;
6146 }
6147 endname = p;
6148 if (name < start) {
6149 p = name;
6150 do {
6151 if (*p == '\\')
6152 p++;
6153 *enddir++ = *p++;
6154 } while (p < start);
6155 }
6156 if (enddir == expdir) {
6157 cp = ".";
6158 } else if (enddir == expdir + 1 && *expdir == '/') {
6159 cp = "/";
6160 } else {
6161 cp = expdir;
6162 enddir[-1] = '\0';
6163 }
6164 dirp = opendir(cp);
6165 if (dirp == NULL)
6166 return;
6167 if (enddir != expdir)
6168 enddir[-1] = '/';
6169 if (*endname == 0) {
6170 atend = 1;
6171 } else {
6172 atend = 0;
6173 *endname++ = '\0';
6174 }
6175 matchdot = 0;
6176 p = start;
6177 if (*p == '\\')
6178 p++;
6179 if (*p == '.')
6180 matchdot++;
6181 while (! intpending && (dp = readdir(dirp)) != NULL) {
6182 if (dp->d_name[0] == '.' && ! matchdot)
6183 continue;
6184 if (pmatch(start, dp->d_name)) {
6185 if (atend) {
6186 strcpy(enddir, dp->d_name);
6187 addfname(expdir);
6188 } else {
6189 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6190 continue;
6191 p[-1] = '/';
6192 expmeta(p, endname);
6193 }
6194 }
6195 }
6196 closedir(dirp);
6197 if (! atend)
6198 endname[-1] = '/';
6199}
6200
6201static struct strlist *
6202msort(struct strlist *list, int len)
6203{
6204 struct strlist *p, *q = NULL;
6205 struct strlist **lpp;
6206 int half;
6207 int n;
6208
6209 if (len <= 1)
6210 return list;
6211 half = len >> 1;
6212 p = list;
6213 for (n = half; --n >= 0; ) {
6214 q = p;
6215 p = p->next;
6216 }
6217 q->next = NULL; /* terminate first half of list */
6218 q = msort(list, half); /* sort first half of list */
6219 p = msort(p, len - half); /* sort second half */
6220 lpp = &list;
6221 for (;;) {
6222#if ENABLE_LOCALE_SUPPORT
6223 if (strcoll(p->text, q->text) < 0)
6224#else
6225 if (strcmp(p->text, q->text) < 0)
6226#endif
6227 {
6228 *lpp = p;
6229 lpp = &p->next;
6230 p = *lpp;
6231 if (p == NULL) {
6232 *lpp = q;
6233 break;
6234 }
6235 } else {
6236 *lpp = q;
6237 lpp = &q->next;
6238 q = *lpp;
6239 if (q == NULL) {
6240 *lpp = p;
6241 break;
6242 }
6243 }
6244 }
6245 return list;
6246}
6247
6248/*
6249 * Sort the results of file name expansion. It calculates the number of
6250 * strings to sort and then calls msort (short for merge sort) to do the
6251 * work.
6252 */
6253static struct strlist *
6254expsort(struct strlist *str)
6255{
6256 int len;
6257 struct strlist *sp;
6258
6259 len = 0;
6260 for (sp = str; sp; sp = sp->next)
6261 len++;
6262 return msort(str, len);
6263}
6264
6265static void
6266expandmeta(struct strlist *str, int flag)
6267{
6268 static const char metachars[] = {
6269 '*', '?', '[', 0
6270 };
6271 /* TODO - EXP_REDIR */
6272
6273 while (str) {
6274 struct strlist **savelastp;
6275 struct strlist *sp;
6276 char *p;
6277
6278 if (fflag)
6279 goto nometa;
6280 if (!strpbrk(str->text, metachars))
6281 goto nometa;
6282 savelastp = exparg.lastp;
6283
6284 INT_OFF;
6285 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6286 {
6287 int i = strlen(str->text);
6288 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6289 }
6290
6291 expmeta(expdir, p);
6292 free(expdir);
6293 if (p != str->text)
6294 free(p);
6295 INT_ON;
6296 if (exparg.lastp == savelastp) {
6297 /*
6298 * no matches
6299 */
6300 nometa:
6301 *exparg.lastp = str;
6302 rmescapes(str->text);
6303 exparg.lastp = &str->next;
6304 } else {
6305 *exparg.lastp = NULL;
6306 *savelastp = sp = expsort(*savelastp);
6307 while (sp->next != NULL)
6308 sp = sp->next;
6309 exparg.lastp = &sp->next;
6310 }
6311 str = str->next;
6312 }
6313}
6314
6315/*
6316 * Perform variable substitution and command substitution on an argument,
6317 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6318 * perform splitting and file name expansion. When arglist is NULL, perform
6319 * here document expansion.
6320 */
6321static void
6322expandarg(union node *arg, struct arglist *arglist, int flag)
6323{
6324 struct strlist *sp;
6325 char *p;
6326
6327 argbackq = arg->narg.backquote;
6328 STARTSTACKSTR(expdest);
6329 ifsfirst.next = NULL;
6330 ifslastp = NULL;
6331 argstr(arg->narg.text, flag);
6332 p = _STPUTC('\0', expdest);
6333 expdest = p - 1;
6334 if (arglist == NULL) {
6335 return; /* here document expanded */
6336 }
6337 p = grabstackstr(p);
6338 exparg.lastp = &exparg.list;
6339 /*
6340 * TODO - EXP_REDIR
6341 */
6342 if (flag & EXP_FULL) {
6343 ifsbreakup(p, &exparg);
6344 *exparg.lastp = NULL;
6345 exparg.lastp = &exparg.list;
6346 expandmeta(exparg.list, flag);
6347 } else {
6348 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6349 rmescapes(p);
6350 sp = stalloc(sizeof(*sp));
6351 sp->text = p;
6352 *exparg.lastp = sp;
6353 exparg.lastp = &sp->next;
6354 }
6355 if (ifsfirst.next)
6356 ifsfree();
6357 *exparg.lastp = NULL;
6358 if (exparg.list) {
6359 *arglist->lastp = exparg.list;
6360 arglist->lastp = exparg.lastp;
6361 }
6362}
6363
6364/*
6365 * Expand shell variables and backquotes inside a here document.
6366 */
6367static void
6368expandhere(union node *arg, int fd)
6369{
6370 herefd = fd;
6371 expandarg(arg, (struct arglist *)NULL, 0);
6372 full_write(fd, stackblock(), expdest - (char *)stackblock());
6373}
6374
6375/*
6376 * Returns true if the pattern matches the string.
6377 */
6378static int
6379patmatch(char *pattern, const char *string)
6380{
6381 return pmatch(preglob(pattern, 0, 0), string);
6382}
6383
6384/*
6385 * See if a pattern matches in a case statement.
6386 */
6387static int
6388casematch(union node *pattern, char *val)
6389{
6390 struct stackmark smark;
6391 int result;
6392
6393 setstackmark(&smark);
6394 argbackq = pattern->narg.backquote;
6395 STARTSTACKSTR(expdest);
6396 ifslastp = NULL;
6397 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
6398 STACKSTRNUL(expdest);
6399 result = patmatch(stackblock(), val);
6400 popstackmark(&smark);
6401 return result;
6402}
6403
6404
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006405/* ============ find_command */
6406
6407struct builtincmd {
6408 const char *name;
6409 int (*builtin)(int, char **);
6410 /* unsigned flags; */
6411};
6412#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
6413#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
6414#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
6415
6416struct cmdentry {
6417 int cmdtype;
6418 union param {
6419 int index;
6420 const struct builtincmd *cmd;
6421 struct funcnode *func;
6422 } u;
6423};
6424/* values of cmdtype */
6425#define CMDUNKNOWN -1 /* no entry in table for command */
6426#define CMDNORMAL 0 /* command is an executable program */
6427#define CMDFUNCTION 1 /* command is a shell function */
6428#define CMDBUILTIN 2 /* command is a shell builtin */
6429
6430/* action to find_command() */
6431#define DO_ERR 0x01 /* prints errors */
6432#define DO_ABS 0x02 /* checks absolute paths */
6433#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6434#define DO_ALTPATH 0x08 /* using alternate path */
6435#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6436
6437static void find_command(char *, struct cmdentry *, int, const char *);
6438
6439
6440/* ============ Hashing commands */
6441
6442/*
6443 * When commands are first encountered, they are entered in a hash table.
6444 * This ensures that a full path search will not have to be done for them
6445 * on each invocation.
6446 *
6447 * We should investigate converting to a linear search, even though that
6448 * would make the command name "hash" a misnomer.
6449 */
6450
6451#define CMDTABLESIZE 31 /* should be prime */
6452#define ARB 1 /* actual size determined at run time */
6453
6454struct tblentry {
6455 struct tblentry *next; /* next entry in hash chain */
6456 union param param; /* definition of builtin function */
6457 short cmdtype; /* index identifying command */
6458 char rehash; /* if set, cd done since entry created */
6459 char cmdname[ARB]; /* name of command */
6460};
6461
6462static struct tblentry *cmdtable[CMDTABLESIZE];
6463static int builtinloc = -1; /* index in path of %builtin, or -1 */
6464
6465static void
6466tryexec(char *cmd, char **argv, char **envp)
6467{
6468 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006469
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006470#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006471 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko335b63d2007-04-10 21:38:30 +00006472 const struct bb_applet *a;
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006473
6474 a = find_applet_by_name(cmd);
6475 if (a) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006476 if (a->noexec) {
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006477 current_applet = a;
Denis Vlasenkof5294e12007-04-14 10:09:57 +00006478 run_current_applet_and_exit(argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006479 }
6480 /* re-exec ourselves with the new arguments */
6481 execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
6482 /* If they called chroot or otherwise made the binary no longer
6483 * executable, fall through */
6484 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006485 }
6486#endif
6487
6488 repeat:
6489#ifdef SYSV
6490 do {
6491 execve(cmd, argv, envp);
6492 } while (errno == EINTR);
6493#else
6494 execve(cmd, argv, envp);
6495#endif
6496 if (repeated++) {
6497 free(argv);
6498 } else if (errno == ENOEXEC) {
6499 char **ap;
6500 char **new;
6501
6502 for (ap = argv; *ap; ap++)
6503 ;
6504 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6505 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006506 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006507 ap += 2;
6508 argv++;
6509 while ((*ap++ = *argv++))
6510 ;
6511 argv = new;
6512 goto repeat;
6513 }
6514}
6515
6516/*
6517 * Exec a program. Never returns. If you change this routine, you may
6518 * have to change the find_command routine as well.
6519 */
6520#define environment() listvars(VEXPORT, VUNSET, 0)
6521static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6522static void
6523shellexec(char **argv, const char *path, int idx)
6524{
6525 char *cmdname;
6526 int e;
6527 char **envp;
6528 int exerrno;
6529
6530 clearredir(1);
6531 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006532 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006533#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006534 || find_applet_by_name(argv[0])
6535#endif
6536 ) {
6537 tryexec(argv[0], argv, envp);
6538 e = errno;
6539 } else {
6540 e = ENOENT;
6541 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6542 if (--idx < 0 && pathopt == NULL) {
6543 tryexec(cmdname, argv, envp);
6544 if (errno != ENOENT && errno != ENOTDIR)
6545 e = errno;
6546 }
6547 stunalloc(cmdname);
6548 }
6549 }
6550
6551 /* Map to POSIX errors */
6552 switch (e) {
6553 case EACCES:
6554 exerrno = 126;
6555 break;
6556 case ENOENT:
6557 exerrno = 127;
6558 break;
6559 default:
6560 exerrno = 2;
6561 break;
6562 }
6563 exitstatus = exerrno;
6564 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6565 argv[0], e, suppressint ));
6566 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6567 /* NOTREACHED */
6568}
6569
6570static void
6571printentry(struct tblentry *cmdp)
6572{
6573 int idx;
6574 const char *path;
6575 char *name;
6576
6577 idx = cmdp->param.index;
6578 path = pathval();
6579 do {
6580 name = padvance(&path, cmdp->cmdname);
6581 stunalloc(name);
6582 } while (--idx >= 0);
6583 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6584}
6585
6586/*
6587 * Clear out command entries. The argument specifies the first entry in
6588 * PATH which has changed.
6589 */
6590static void
6591clearcmdentry(int firstchange)
6592{
6593 struct tblentry **tblp;
6594 struct tblentry **pp;
6595 struct tblentry *cmdp;
6596
6597 INT_OFF;
6598 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6599 pp = tblp;
6600 while ((cmdp = *pp) != NULL) {
6601 if ((cmdp->cmdtype == CMDNORMAL &&
6602 cmdp->param.index >= firstchange)
6603 || (cmdp->cmdtype == CMDBUILTIN &&
6604 builtinloc >= firstchange)
6605 ) {
6606 *pp = cmdp->next;
6607 free(cmdp);
6608 } else {
6609 pp = &cmdp->next;
6610 }
6611 }
6612 }
6613 INT_ON;
6614}
6615
6616/*
6617 * Locate a command in the command hash table. If "add" is nonzero,
6618 * add the command to the table if it is not already present. The
6619 * variable "lastcmdentry" is set to point to the address of the link
6620 * pointing to the entry, so that delete_cmd_entry can delete the
6621 * entry.
6622 *
6623 * Interrupts must be off if called with add != 0.
6624 */
6625static struct tblentry **lastcmdentry;
6626
6627static struct tblentry *
6628cmdlookup(const char *name, int add)
6629{
6630 unsigned int hashval;
6631 const char *p;
6632 struct tblentry *cmdp;
6633 struct tblentry **pp;
6634
6635 p = name;
6636 hashval = (unsigned char)*p << 4;
6637 while (*p)
6638 hashval += (unsigned char)*p++;
6639 hashval &= 0x7FFF;
6640 pp = &cmdtable[hashval % CMDTABLESIZE];
6641 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6642 if (strcmp(cmdp->cmdname, name) == 0)
6643 break;
6644 pp = &cmdp->next;
6645 }
6646 if (add && cmdp == NULL) {
6647 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6648 + strlen(name) + 1);
6649 cmdp->next = NULL;
6650 cmdp->cmdtype = CMDUNKNOWN;
6651 strcpy(cmdp->cmdname, name);
6652 }
6653 lastcmdentry = pp;
6654 return cmdp;
6655}
6656
6657/*
6658 * Delete the command entry returned on the last lookup.
6659 */
6660static void
6661delete_cmd_entry(void)
6662{
6663 struct tblentry *cmdp;
6664
6665 INT_OFF;
6666 cmdp = *lastcmdentry;
6667 *lastcmdentry = cmdp->next;
6668 if (cmdp->cmdtype == CMDFUNCTION)
6669 freefunc(cmdp->param.func);
6670 free(cmdp);
6671 INT_ON;
6672}
6673
6674/*
6675 * Add a new command entry, replacing any existing command entry for
6676 * the same name - except special builtins.
6677 */
6678static void
6679addcmdentry(char *name, struct cmdentry *entry)
6680{
6681 struct tblentry *cmdp;
6682
6683 cmdp = cmdlookup(name, 1);
6684 if (cmdp->cmdtype == CMDFUNCTION) {
6685 freefunc(cmdp->param.func);
6686 }
6687 cmdp->cmdtype = entry->cmdtype;
6688 cmdp->param = entry->u;
6689 cmdp->rehash = 0;
6690}
6691
6692static int
6693hashcmd(int argc, char **argv)
6694{
6695 struct tblentry **pp;
6696 struct tblentry *cmdp;
6697 int c;
6698 struct cmdentry entry;
6699 char *name;
6700
6701 while ((c = nextopt("r")) != '\0') {
6702 clearcmdentry(0);
6703 return 0;
6704 }
6705 if (*argptr == NULL) {
6706 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6707 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6708 if (cmdp->cmdtype == CMDNORMAL)
6709 printentry(cmdp);
6710 }
6711 }
6712 return 0;
6713 }
6714 c = 0;
6715 while ((name = *argptr) != NULL) {
6716 cmdp = cmdlookup(name, 0);
6717 if (cmdp != NULL
6718 && (cmdp->cmdtype == CMDNORMAL
6719 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
6720 delete_cmd_entry();
6721 find_command(name, &entry, DO_ERR, pathval());
6722 if (entry.cmdtype == CMDUNKNOWN)
6723 c = 1;
6724 argptr++;
6725 }
6726 return c;
6727}
6728
6729/*
6730 * Called when a cd is done. Marks all commands so the next time they
6731 * are executed they will be rehashed.
6732 */
6733static void
6734hashcd(void)
6735{
6736 struct tblentry **pp;
6737 struct tblentry *cmdp;
6738
6739 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6740 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6741 if (cmdp->cmdtype == CMDNORMAL || (
6742 cmdp->cmdtype == CMDBUILTIN &&
6743 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
6744 builtinloc > 0
6745 ))
6746 cmdp->rehash = 1;
6747 }
6748 }
6749}
6750
6751/*
6752 * Fix command hash table when PATH changed.
6753 * Called before PATH is changed. The argument is the new value of PATH;
6754 * pathval() still returns the old value at this point.
6755 * Called with interrupts off.
6756 */
6757static void
6758changepath(const char *newval)
6759{
6760 const char *old, *new;
6761 int idx;
6762 int firstchange;
6763 int idx_bltin;
6764
6765 old = pathval();
6766 new = newval;
6767 firstchange = 9999; /* assume no change */
6768 idx = 0;
6769 idx_bltin = -1;
6770 for (;;) {
6771 if (*old != *new) {
6772 firstchange = idx;
6773 if ((*old == '\0' && *new == ':')
6774 || (*old == ':' && *new == '\0'))
6775 firstchange++;
6776 old = new; /* ignore subsequent differences */
6777 }
6778 if (*new == '\0')
6779 break;
6780 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6781 idx_bltin = idx;
6782 if (*new == ':') {
6783 idx++;
6784 }
6785 new++, old++;
6786 }
6787 if (builtinloc < 0 && idx_bltin >= 0)
6788 builtinloc = idx_bltin; /* zap builtins */
6789 if (builtinloc >= 0 && idx_bltin < 0)
6790 firstchange = 0;
6791 clearcmdentry(firstchange);
6792 builtinloc = idx_bltin;
6793}
6794
6795#define TEOF 0
6796#define TNL 1
6797#define TREDIR 2
6798#define TWORD 3
6799#define TSEMI 4
6800#define TBACKGND 5
6801#define TAND 6
6802#define TOR 7
6803#define TPIPE 8
6804#define TLP 9
6805#define TRP 10
6806#define TENDCASE 11
6807#define TENDBQUOTE 12
6808#define TNOT 13
6809#define TCASE 14
6810#define TDO 15
6811#define TDONE 16
6812#define TELIF 17
6813#define TELSE 18
6814#define TESAC 19
6815#define TFI 20
6816#define TFOR 21
6817#define TIF 22
6818#define TIN 23
6819#define TTHEN 24
6820#define TUNTIL 25
6821#define TWHILE 26
6822#define TBEGIN 27
6823#define TEND 28
6824
6825/* first char is indicating which tokens mark the end of a list */
6826static const char *const tokname_array[] = {
6827 "\1end of file",
6828 "\0newline",
6829 "\0redirection",
6830 "\0word",
6831 "\0;",
6832 "\0&",
6833 "\0&&",
6834 "\0||",
6835 "\0|",
6836 "\0(",
6837 "\1)",
6838 "\1;;",
6839 "\1`",
6840#define KWDOFFSET 13
6841 /* the following are keywords */
6842 "\0!",
6843 "\0case",
6844 "\1do",
6845 "\1done",
6846 "\1elif",
6847 "\1else",
6848 "\1esac",
6849 "\1fi",
6850 "\0for",
6851 "\0if",
6852 "\0in",
6853 "\1then",
6854 "\0until",
6855 "\0while",
6856 "\0{",
6857 "\1}",
6858};
6859
6860static const char *
6861tokname(int tok)
6862{
6863 static char buf[16];
6864
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006865//try this:
6866//if (tok < TSEMI) return tokname_array[tok] + 1;
6867//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
6868//return buf;
6869
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006870 if (tok >= TSEMI)
6871 buf[0] = '"';
6872 sprintf(buf + (tok >= TSEMI), "%s%c",
6873 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
6874 return buf;
6875}
6876
6877/* Wrapper around strcmp for qsort/bsearch/... */
6878static int
6879pstrcmp(const void *a, const void *b)
6880{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006881 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006882}
6883
6884static const char *const *
6885findkwd(const char *s)
6886{
6887 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006888 (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
6889 sizeof(char *), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006890}
6891
6892/*
6893 * Locate and print what a word is...
6894 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006895static int
6896describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006897{
6898 struct cmdentry entry;
6899 struct tblentry *cmdp;
6900#if ENABLE_ASH_ALIAS
6901 const struct alias *ap;
6902#endif
6903 const char *path = pathval();
6904
6905 if (describe_command_verbose) {
6906 out1str(command);
6907 }
6908
6909 /* First look at the keywords */
6910 if (findkwd(command)) {
6911 out1str(describe_command_verbose ? " is a shell keyword" : command);
6912 goto out;
6913 }
6914
6915#if ENABLE_ASH_ALIAS
6916 /* Then look at the aliases */
6917 ap = lookupalias(command, 0);
6918 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00006919 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006920 out1str("alias ");
6921 printalias(ap);
6922 return 0;
6923 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00006924 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006925 goto out;
6926 }
6927#endif
6928 /* Then check if it is a tracked alias */
6929 cmdp = cmdlookup(command, 0);
6930 if (cmdp != NULL) {
6931 entry.cmdtype = cmdp->cmdtype;
6932 entry.u = cmdp->param;
6933 } else {
6934 /* Finally use brute force */
6935 find_command(command, &entry, DO_ABS, path);
6936 }
6937
6938 switch (entry.cmdtype) {
6939 case CMDNORMAL: {
6940 int j = entry.u.index;
6941 char *p;
6942 if (j == -1) {
6943 p = command;
6944 } else {
6945 do {
6946 p = padvance(&path, command);
6947 stunalloc(p);
6948 } while (--j >= 0);
6949 }
6950 if (describe_command_verbose) {
6951 out1fmt(" is%s %s",
6952 (cmdp ? " a tracked alias for" : nullstr), p
6953 );
6954 } else {
6955 out1str(p);
6956 }
6957 break;
6958 }
6959
6960 case CMDFUNCTION:
6961 if (describe_command_verbose) {
6962 out1str(" is a shell function");
6963 } else {
6964 out1str(command);
6965 }
6966 break;
6967
6968 case CMDBUILTIN:
6969 if (describe_command_verbose) {
6970 out1fmt(" is a %sshell builtin",
6971 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
6972 "special " : nullstr
6973 );
6974 } else {
6975 out1str(command);
6976 }
6977 break;
6978
6979 default:
6980 if (describe_command_verbose) {
6981 out1str(": not found\n");
6982 }
6983 return 127;
6984 }
6985 out:
6986 outstr("\n", stdout);
6987 return 0;
6988}
6989
6990static int
6991typecmd(int argc, char **argv)
6992{
Denis Vlasenko46846e22007-05-20 13:08:31 +00006993 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006994 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00006995 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006996
Denis Vlasenko46846e22007-05-20 13:08:31 +00006997 /* type -p ... ? (we don't bother checking for 'p') */
6998 if (argv[1][0] == '-') {
6999 i++;
7000 verbose = 0;
7001 }
7002 while (i < argc) {
7003 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007004 }
7005 return err;
7006}
7007
7008#if ENABLE_ASH_CMDCMD
7009static int
7010commandcmd(int argc, char **argv)
7011{
7012 int c;
7013 enum {
7014 VERIFY_BRIEF = 1,
7015 VERIFY_VERBOSE = 2,
7016 } verify = 0;
7017
7018 while ((c = nextopt("pvV")) != '\0')
7019 if (c == 'V')
7020 verify |= VERIFY_VERBOSE;
7021 else if (c == 'v')
7022 verify |= VERIFY_BRIEF;
7023#if DEBUG
7024 else if (c != 'p')
7025 abort();
7026#endif
7027 if (verify)
7028 return describe_command(*argptr, verify - VERIFY_BRIEF);
7029
7030 return 0;
7031}
7032#endif
7033
7034
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007035/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007036
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007037static int funcblocksize; /* size of structures in function */
7038static int funcstringsize; /* size of strings in node */
7039static void *funcblock; /* block to allocate function from */
7040static char *funcstring; /* block to allocate strings from */
7041
Eric Andersencb57d552001-06-28 07:25:16 +00007042/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007043#define EV_EXIT 01 /* exit after evaluating tree */
7044#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7045#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007046
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007047static const short nodesize[26] = {
7048 SHELL_ALIGN(sizeof(struct ncmd)),
7049 SHELL_ALIGN(sizeof(struct npipe)),
7050 SHELL_ALIGN(sizeof(struct nredir)),
7051 SHELL_ALIGN(sizeof(struct nredir)),
7052 SHELL_ALIGN(sizeof(struct nredir)),
7053 SHELL_ALIGN(sizeof(struct nbinary)),
7054 SHELL_ALIGN(sizeof(struct nbinary)),
7055 SHELL_ALIGN(sizeof(struct nbinary)),
7056 SHELL_ALIGN(sizeof(struct nif)),
7057 SHELL_ALIGN(sizeof(struct nbinary)),
7058 SHELL_ALIGN(sizeof(struct nbinary)),
7059 SHELL_ALIGN(sizeof(struct nfor)),
7060 SHELL_ALIGN(sizeof(struct ncase)),
7061 SHELL_ALIGN(sizeof(struct nclist)),
7062 SHELL_ALIGN(sizeof(struct narg)),
7063 SHELL_ALIGN(sizeof(struct narg)),
7064 SHELL_ALIGN(sizeof(struct nfile)),
7065 SHELL_ALIGN(sizeof(struct nfile)),
7066 SHELL_ALIGN(sizeof(struct nfile)),
7067 SHELL_ALIGN(sizeof(struct nfile)),
7068 SHELL_ALIGN(sizeof(struct nfile)),
7069 SHELL_ALIGN(sizeof(struct ndup)),
7070 SHELL_ALIGN(sizeof(struct ndup)),
7071 SHELL_ALIGN(sizeof(struct nhere)),
7072 SHELL_ALIGN(sizeof(struct nhere)),
7073 SHELL_ALIGN(sizeof(struct nnot)),
7074};
7075
7076static void calcsize(union node *n);
7077
7078static void
7079sizenodelist(struct nodelist *lp)
7080{
7081 while (lp) {
7082 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7083 calcsize(lp->n);
7084 lp = lp->next;
7085 }
7086}
7087
7088static void
7089calcsize(union node *n)
7090{
7091 if (n == NULL)
7092 return;
7093 funcblocksize += nodesize[n->type];
7094 switch (n->type) {
7095 case NCMD:
7096 calcsize(n->ncmd.redirect);
7097 calcsize(n->ncmd.args);
7098 calcsize(n->ncmd.assign);
7099 break;
7100 case NPIPE:
7101 sizenodelist(n->npipe.cmdlist);
7102 break;
7103 case NREDIR:
7104 case NBACKGND:
7105 case NSUBSHELL:
7106 calcsize(n->nredir.redirect);
7107 calcsize(n->nredir.n);
7108 break;
7109 case NAND:
7110 case NOR:
7111 case NSEMI:
7112 case NWHILE:
7113 case NUNTIL:
7114 calcsize(n->nbinary.ch2);
7115 calcsize(n->nbinary.ch1);
7116 break;
7117 case NIF:
7118 calcsize(n->nif.elsepart);
7119 calcsize(n->nif.ifpart);
7120 calcsize(n->nif.test);
7121 break;
7122 case NFOR:
7123 funcstringsize += strlen(n->nfor.var) + 1;
7124 calcsize(n->nfor.body);
7125 calcsize(n->nfor.args);
7126 break;
7127 case NCASE:
7128 calcsize(n->ncase.cases);
7129 calcsize(n->ncase.expr);
7130 break;
7131 case NCLIST:
7132 calcsize(n->nclist.body);
7133 calcsize(n->nclist.pattern);
7134 calcsize(n->nclist.next);
7135 break;
7136 case NDEFUN:
7137 case NARG:
7138 sizenodelist(n->narg.backquote);
7139 funcstringsize += strlen(n->narg.text) + 1;
7140 calcsize(n->narg.next);
7141 break;
7142 case NTO:
7143 case NCLOBBER:
7144 case NFROM:
7145 case NFROMTO:
7146 case NAPPEND:
7147 calcsize(n->nfile.fname);
7148 calcsize(n->nfile.next);
7149 break;
7150 case NTOFD:
7151 case NFROMFD:
7152 calcsize(n->ndup.vname);
7153 calcsize(n->ndup.next);
7154 break;
7155 case NHERE:
7156 case NXHERE:
7157 calcsize(n->nhere.doc);
7158 calcsize(n->nhere.next);
7159 break;
7160 case NNOT:
7161 calcsize(n->nnot.com);
7162 break;
7163 };
7164}
7165
7166static char *
7167nodeckstrdup(char *s)
7168{
7169 char *rtn = funcstring;
7170
7171 strcpy(funcstring, s);
7172 funcstring += strlen(s) + 1;
7173 return rtn;
7174}
7175
7176static union node *copynode(union node *);
7177
7178static struct nodelist *
7179copynodelist(struct nodelist *lp)
7180{
7181 struct nodelist *start;
7182 struct nodelist **lpp;
7183
7184 lpp = &start;
7185 while (lp) {
7186 *lpp = funcblock;
7187 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7188 (*lpp)->n = copynode(lp->n);
7189 lp = lp->next;
7190 lpp = &(*lpp)->next;
7191 }
7192 *lpp = NULL;
7193 return start;
7194}
7195
7196static union node *
7197copynode(union node *n)
7198{
7199 union node *new;
7200
7201 if (n == NULL)
7202 return NULL;
7203 new = funcblock;
7204 funcblock = (char *) funcblock + nodesize[n->type];
7205
7206 switch (n->type) {
7207 case NCMD:
7208 new->ncmd.redirect = copynode(n->ncmd.redirect);
7209 new->ncmd.args = copynode(n->ncmd.args);
7210 new->ncmd.assign = copynode(n->ncmd.assign);
7211 break;
7212 case NPIPE:
7213 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7214 new->npipe.backgnd = n->npipe.backgnd;
7215 break;
7216 case NREDIR:
7217 case NBACKGND:
7218 case NSUBSHELL:
7219 new->nredir.redirect = copynode(n->nredir.redirect);
7220 new->nredir.n = copynode(n->nredir.n);
7221 break;
7222 case NAND:
7223 case NOR:
7224 case NSEMI:
7225 case NWHILE:
7226 case NUNTIL:
7227 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7228 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7229 break;
7230 case NIF:
7231 new->nif.elsepart = copynode(n->nif.elsepart);
7232 new->nif.ifpart = copynode(n->nif.ifpart);
7233 new->nif.test = copynode(n->nif.test);
7234 break;
7235 case NFOR:
7236 new->nfor.var = nodeckstrdup(n->nfor.var);
7237 new->nfor.body = copynode(n->nfor.body);
7238 new->nfor.args = copynode(n->nfor.args);
7239 break;
7240 case NCASE:
7241 new->ncase.cases = copynode(n->ncase.cases);
7242 new->ncase.expr = copynode(n->ncase.expr);
7243 break;
7244 case NCLIST:
7245 new->nclist.body = copynode(n->nclist.body);
7246 new->nclist.pattern = copynode(n->nclist.pattern);
7247 new->nclist.next = copynode(n->nclist.next);
7248 break;
7249 case NDEFUN:
7250 case NARG:
7251 new->narg.backquote = copynodelist(n->narg.backquote);
7252 new->narg.text = nodeckstrdup(n->narg.text);
7253 new->narg.next = copynode(n->narg.next);
7254 break;
7255 case NTO:
7256 case NCLOBBER:
7257 case NFROM:
7258 case NFROMTO:
7259 case NAPPEND:
7260 new->nfile.fname = copynode(n->nfile.fname);
7261 new->nfile.fd = n->nfile.fd;
7262 new->nfile.next = copynode(n->nfile.next);
7263 break;
7264 case NTOFD:
7265 case NFROMFD:
7266 new->ndup.vname = copynode(n->ndup.vname);
7267 new->ndup.dupfd = n->ndup.dupfd;
7268 new->ndup.fd = n->ndup.fd;
7269 new->ndup.next = copynode(n->ndup.next);
7270 break;
7271 case NHERE:
7272 case NXHERE:
7273 new->nhere.doc = copynode(n->nhere.doc);
7274 new->nhere.fd = n->nhere.fd;
7275 new->nhere.next = copynode(n->nhere.next);
7276 break;
7277 case NNOT:
7278 new->nnot.com = copynode(n->nnot.com);
7279 break;
7280 };
7281 new->type = n->type;
7282 return new;
7283}
7284
7285/*
7286 * Make a copy of a parse tree.
7287 */
7288static struct funcnode *
7289copyfunc(union node *n)
7290{
7291 struct funcnode *f;
7292 size_t blocksize;
7293
7294 funcblocksize = offsetof(struct funcnode, n);
7295 funcstringsize = 0;
7296 calcsize(n);
7297 blocksize = funcblocksize;
7298 f = ckmalloc(blocksize + funcstringsize);
7299 funcblock = (char *) f + offsetof(struct funcnode, n);
7300 funcstring = (char *) f + blocksize;
7301 copynode(n);
7302 f->count = 0;
7303 return f;
7304}
7305
7306/*
7307 * Define a shell function.
7308 */
7309static void
7310defun(char *name, union node *func)
7311{
7312 struct cmdentry entry;
7313
7314 INT_OFF;
7315 entry.cmdtype = CMDFUNCTION;
7316 entry.u.func = copyfunc(func);
7317 addcmdentry(name, &entry);
7318 INT_ON;
7319}
7320
7321static int evalskip; /* set if we are skipping commands */
7322/* reasons for skipping commands (see comment on breakcmd routine) */
7323#define SKIPBREAK (1 << 0)
7324#define SKIPCONT (1 << 1)
7325#define SKIPFUNC (1 << 2)
7326#define SKIPFILE (1 << 3)
7327#define SKIPEVAL (1 << 4)
7328static int skipcount; /* number of levels to skip */
7329static int funcnest; /* depth of function calls */
7330
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007331/* forward decl way out to parsing code - dotrap needs it */
7332static int evalstring(char *s, int mask);
7333
7334/*
7335 * Called to execute a trap. Perhaps we should avoid entering new trap
7336 * handlers while we are executing a trap handler.
7337 */
7338static int
7339dotrap(void)
7340{
7341 char *p;
7342 char *q;
7343 int i;
7344 int savestatus;
7345 int skip = 0;
7346
7347 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007348 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007349 xbarrier();
7350
7351 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7352 if (!*q)
7353 continue;
7354 *q = '\0';
7355
7356 p = trap[i + 1];
7357 if (!p)
7358 continue;
7359 skip = evalstring(p, SKIPEVAL);
7360 exitstatus = savestatus;
7361 if (skip)
7362 break;
7363 }
7364
7365 return skip;
7366}
7367
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007368/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007369static void evalloop(union node *, int);
7370static void evalfor(union node *, int);
7371static void evalcase(union node *, int);
7372static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007373static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007374static void evalpipe(union node *, int);
7375static void evalcommand(union node *, int);
7376static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007377static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007378
Eric Andersen62483552001-07-10 06:09:16 +00007379/*
Eric Andersenc470f442003-07-28 09:56:35 +00007380 * Evaluate a parse tree. The value is left in the global variable
7381 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007382 */
Eric Andersenc470f442003-07-28 09:56:35 +00007383static void
7384evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007385{
Eric Andersenc470f442003-07-28 09:56:35 +00007386 int checkexit = 0;
7387 void (*evalfn)(union node *, int);
7388 unsigned isor;
7389 int status;
7390 if (n == NULL) {
7391 TRACE(("evaltree(NULL) called\n"));
7392 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007393 }
Eric Andersenc470f442003-07-28 09:56:35 +00007394 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007395 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007396 switch (n->type) {
7397 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007398#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007399 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007400 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007401 break;
7402#endif
7403 case NNOT:
7404 evaltree(n->nnot.com, EV_TESTED);
7405 status = !exitstatus;
7406 goto setstatus;
7407 case NREDIR:
7408 expredir(n->nredir.redirect);
7409 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7410 if (!status) {
7411 evaltree(n->nredir.n, flags & EV_TESTED);
7412 status = exitstatus;
7413 }
7414 popredir(0);
7415 goto setstatus;
7416 case NCMD:
7417 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007418 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007419 if (eflag && !(flags & EV_TESTED))
7420 checkexit = ~0;
7421 goto calleval;
7422 case NFOR:
7423 evalfn = evalfor;
7424 goto calleval;
7425 case NWHILE:
7426 case NUNTIL:
7427 evalfn = evalloop;
7428 goto calleval;
7429 case NSUBSHELL:
7430 case NBACKGND:
7431 evalfn = evalsubshell;
7432 goto calleval;
7433 case NPIPE:
7434 evalfn = evalpipe;
7435 goto checkexit;
7436 case NCASE:
7437 evalfn = evalcase;
7438 goto calleval;
7439 case NAND:
7440 case NOR:
7441 case NSEMI:
7442#if NAND + 1 != NOR
7443#error NAND + 1 != NOR
7444#endif
7445#if NOR + 1 != NSEMI
7446#error NOR + 1 != NSEMI
7447#endif
7448 isor = n->type - NAND;
7449 evaltree(
7450 n->nbinary.ch1,
7451 (flags | ((isor >> 1) - 1)) & EV_TESTED
7452 );
7453 if (!exitstatus == isor)
7454 break;
7455 if (!evalskip) {
7456 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007457 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007458 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007459 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007460 evalfn(n, flags);
7461 break;
7462 }
7463 break;
7464 case NIF:
7465 evaltree(n->nif.test, EV_TESTED);
7466 if (evalskip)
7467 break;
7468 if (exitstatus == 0) {
7469 n = n->nif.ifpart;
7470 goto evaln;
7471 } else if (n->nif.elsepart) {
7472 n = n->nif.elsepart;
7473 goto evaln;
7474 }
7475 goto success;
7476 case NDEFUN:
7477 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007478 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007479 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007480 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007481 exitstatus = status;
7482 break;
7483 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007484 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007485 if ((checkexit & exitstatus))
7486 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007487 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007488 goto exexit;
7489
7490 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007491 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007492 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007493 }
Eric Andersen62483552001-07-10 06:09:16 +00007494}
7495
Eric Andersenc470f442003-07-28 09:56:35 +00007496#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7497static
7498#endif
7499void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7500
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007501static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007502
7503static void
7504evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007505{
7506 int status;
7507
7508 loopnest++;
7509 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007510 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007511 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007512 int i;
7513
Eric Andersencb57d552001-06-28 07:25:16 +00007514 evaltree(n->nbinary.ch1, EV_TESTED);
7515 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007516 skipping:
7517 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007518 evalskip = 0;
7519 continue;
7520 }
7521 if (evalskip == SKIPBREAK && --skipcount <= 0)
7522 evalskip = 0;
7523 break;
7524 }
Eric Andersenc470f442003-07-28 09:56:35 +00007525 i = exitstatus;
7526 if (n->type != NWHILE)
7527 i = !i;
7528 if (i != 0)
7529 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007530 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007531 status = exitstatus;
7532 if (evalskip)
7533 goto skipping;
7534 }
7535 loopnest--;
7536 exitstatus = status;
7537}
7538
Eric Andersenc470f442003-07-28 09:56:35 +00007539static void
7540evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007541{
7542 struct arglist arglist;
7543 union node *argp;
7544 struct strlist *sp;
7545 struct stackmark smark;
7546
7547 setstackmark(&smark);
7548 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007549 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007550 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007551 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007552 if (evalskip)
7553 goto out;
7554 }
7555 *arglist.lastp = NULL;
7556
7557 exitstatus = 0;
7558 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007559 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007560 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007561 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007562 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007563 if (evalskip) {
7564 if (evalskip == SKIPCONT && --skipcount <= 0) {
7565 evalskip = 0;
7566 continue;
7567 }
7568 if (evalskip == SKIPBREAK && --skipcount <= 0)
7569 evalskip = 0;
7570 break;
7571 }
7572 }
7573 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007574 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007575 popstackmark(&smark);
7576}
7577
Eric Andersenc470f442003-07-28 09:56:35 +00007578static void
7579evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007580{
7581 union node *cp;
7582 union node *patp;
7583 struct arglist arglist;
7584 struct stackmark smark;
7585
7586 setstackmark(&smark);
7587 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007588 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007589 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007590 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7591 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007592 if (casematch(patp, arglist.list->text)) {
7593 if (evalskip == 0) {
7594 evaltree(cp->nclist.body, flags);
7595 }
7596 goto out;
7597 }
7598 }
7599 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007600 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007601 popstackmark(&smark);
7602}
7603
Eric Andersenc470f442003-07-28 09:56:35 +00007604/*
7605 * Kick off a subshell to evaluate a tree.
7606 */
Eric Andersenc470f442003-07-28 09:56:35 +00007607static void
7608evalsubshell(union node *n, int flags)
7609{
7610 struct job *jp;
7611 int backgnd = (n->type == NBACKGND);
7612 int status;
7613
7614 expredir(n->nredir.redirect);
7615 if (!backgnd && flags & EV_EXIT && !trap[0])
7616 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007617 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007618 jp = makejob(n, 1);
7619 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007620 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007621 flags |= EV_EXIT;
7622 if (backgnd)
7623 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007624 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007625 redirect(n->nredir.redirect, 0);
7626 evaltreenr(n->nredir.n, flags);
7627 /* never returns */
7628 }
7629 status = 0;
7630 if (! backgnd)
7631 status = waitforjob(jp);
7632 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007633 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007634}
7635
Eric Andersenc470f442003-07-28 09:56:35 +00007636/*
7637 * Compute the names of the files in a redirection list.
7638 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007639static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007640static void
7641expredir(union node *n)
7642{
7643 union node *redir;
7644
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007645 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007646 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007647
7648 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007649 fn.lastp = &fn.list;
7650 switch (redir->type) {
7651 case NFROMTO:
7652 case NFROM:
7653 case NTO:
7654 case NCLOBBER:
7655 case NAPPEND:
7656 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7657 redir->nfile.expfname = fn.list->text;
7658 break;
7659 case NFROMFD:
7660 case NTOFD:
7661 if (redir->ndup.vname) {
7662 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007663 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007664 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007665 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007666 }
7667 break;
7668 }
7669 }
7670}
7671
Eric Andersencb57d552001-06-28 07:25:16 +00007672/*
Eric Andersencb57d552001-06-28 07:25:16 +00007673 * Evaluate a pipeline. All the processes in the pipeline are children
7674 * of the process creating the pipeline. (This differs from some versions
7675 * of the shell, which make the last process in a pipeline the parent
7676 * of all the rest.)
7677 */
Eric Andersenc470f442003-07-28 09:56:35 +00007678static void
7679evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007680{
7681 struct job *jp;
7682 struct nodelist *lp;
7683 int pipelen;
7684 int prevfd;
7685 int pip[2];
7686
Eric Andersenc470f442003-07-28 09:56:35 +00007687 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007688 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007689 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007690 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007691 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007692 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007693 jp = makejob(n, pipelen);
7694 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007695 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007696 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007697 pip[1] = -1;
7698 if (lp->next) {
7699 if (pipe(pip) < 0) {
7700 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007701 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007702 }
7703 }
7704 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007705 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007706 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007707 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007708 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007709 if (prevfd > 0) {
7710 dup2(prevfd, 0);
7711 close(prevfd);
7712 }
7713 if (pip[1] > 1) {
7714 dup2(pip[1], 1);
7715 close(pip[1]);
7716 }
Eric Andersenc470f442003-07-28 09:56:35 +00007717 evaltreenr(lp->n, flags);
7718 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007719 }
7720 if (prevfd >= 0)
7721 close(prevfd);
7722 prevfd = pip[0];
7723 close(pip[1]);
7724 }
Eric Andersencb57d552001-06-28 07:25:16 +00007725 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007726 exitstatus = waitforjob(jp);
7727 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007728 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007729 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007730}
7731
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007732/*
7733 * Controls whether the shell is interactive or not.
7734 */
7735static void
7736setinteractive(int on)
7737{
7738 static int is_interactive;
7739
7740 if (++on == is_interactive)
7741 return;
7742 is_interactive = on;
7743 setsignal(SIGINT);
7744 setsignal(SIGQUIT);
7745 setsignal(SIGTERM);
7746#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7747 if (is_interactive > 1) {
7748 /* Looks like they want an interactive shell */
7749 static smallint do_banner;
7750
7751 if (!do_banner) {
7752 out1fmt(
7753 "\n\n"
7754 "%s Built-in shell (ash)\n"
7755 "Enter 'help' for a list of built-in commands."
7756 "\n\n",
7757 BB_BANNER);
7758 do_banner = 1;
7759 }
7760 }
7761#endif
7762}
7763
7764#if ENABLE_FEATURE_EDITING_VI
7765#define setvimode(on) do { \
7766 if (on) line_input_state->flags |= VI_MODE; \
7767 else line_input_state->flags &= ~VI_MODE; \
7768} while (0)
7769#else
7770#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7771#endif
7772
7773static void
7774optschanged(void)
7775{
7776#if DEBUG
7777 opentrace();
7778#endif
7779 setinteractive(iflag);
7780 setjobctl(mflag);
7781 setvimode(viflag);
7782}
7783
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007784static struct localvar *localvars;
7785
7786/*
7787 * Called after a function returns.
7788 * Interrupts must be off.
7789 */
7790static void
7791poplocalvars(void)
7792{
7793 struct localvar *lvp;
7794 struct var *vp;
7795
7796 while ((lvp = localvars) != NULL) {
7797 localvars = lvp->next;
7798 vp = lvp->vp;
7799 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7800 if (vp == NULL) { /* $- saved */
7801 memcpy(optlist, lvp->text, sizeof(optlist));
7802 free((char*)lvp->text);
7803 optschanged();
7804 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7805 unsetvar(vp->text);
7806 } else {
7807 if (vp->func)
7808 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7809 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7810 free((char*)vp->text);
7811 vp->flags = lvp->flags;
7812 vp->text = lvp->text;
7813 }
7814 free(lvp);
7815 }
7816}
7817
7818static int
7819evalfun(struct funcnode *func, int argc, char **argv, int flags)
7820{
7821 volatile struct shparam saveparam;
7822 struct localvar *volatile savelocalvars;
7823 struct jmploc *volatile savehandler;
7824 struct jmploc jmploc;
7825 int e;
7826
7827 saveparam = shellparam;
7828 savelocalvars = localvars;
7829 e = setjmp(jmploc.loc);
7830 if (e) {
7831 goto funcdone;
7832 }
7833 INT_OFF;
7834 savehandler = exception_handler;
7835 exception_handler = &jmploc;
7836 localvars = NULL;
7837 shellparam.malloc = 0;
7838 func->count++;
7839 funcnest++;
7840 INT_ON;
7841 shellparam.nparam = argc - 1;
7842 shellparam.p = argv + 1;
7843#if ENABLE_ASH_GETOPTS
7844 shellparam.optind = 1;
7845 shellparam.optoff = -1;
7846#endif
7847 evaltree(&func->n, flags & EV_TESTED);
7848funcdone:
7849 INT_OFF;
7850 funcnest--;
7851 freefunc(func);
7852 poplocalvars();
7853 localvars = savelocalvars;
7854 freeparam(&shellparam);
7855 shellparam = saveparam;
7856 exception_handler = savehandler;
7857 INT_ON;
7858 evalskip &= ~SKIPFUNC;
7859 return e;
7860}
7861
Denis Vlasenko131ae172007-02-18 13:00:19 +00007862#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007863static char **
7864parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007865{
7866 char *cp, c;
7867
7868 for (;;) {
7869 cp = *++argv;
7870 if (!cp)
7871 return 0;
7872 if (*cp++ != '-')
7873 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007874 c = *cp++;
7875 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00007876 break;
7877 if (c == '-' && !*cp) {
7878 argv++;
7879 break;
7880 }
7881 do {
7882 switch (c) {
7883 case 'p':
7884 *path = defpath;
7885 break;
7886 default:
7887 /* run 'typecmd' for other options */
7888 return 0;
7889 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00007890 c = *cp++;
7891 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00007892 }
7893 return argv;
7894}
7895#endif
7896
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007897/*
7898 * Make a variable a local variable. When a variable is made local, it's
7899 * value and flags are saved in a localvar structure. The saved values
7900 * will be restored when the shell function returns. We handle the name
7901 * "-" as a special case.
7902 */
7903static void
7904mklocal(char *name)
7905{
7906 struct localvar *lvp;
7907 struct var **vpp;
7908 struct var *vp;
7909
7910 INT_OFF;
7911 lvp = ckmalloc(sizeof(struct localvar));
7912 if (LONE_DASH(name)) {
7913 char *p;
7914 p = ckmalloc(sizeof(optlist));
7915 lvp->text = memcpy(p, optlist, sizeof(optlist));
7916 vp = NULL;
7917 } else {
7918 char *eq;
7919
7920 vpp = hashvar(name);
7921 vp = *findvar(vpp, name);
7922 eq = strchr(name, '=');
7923 if (vp == NULL) {
7924 if (eq)
7925 setvareq(name, VSTRFIXED);
7926 else
7927 setvar(name, NULL, VSTRFIXED);
7928 vp = *vpp; /* the new variable */
7929 lvp->flags = VUNSET;
7930 } else {
7931 lvp->text = vp->text;
7932 lvp->flags = vp->flags;
7933 vp->flags |= VSTRFIXED|VTEXTFIXED;
7934 if (eq)
7935 setvareq(name, 0);
7936 }
7937 }
7938 lvp->vp = vp;
7939 lvp->next = localvars;
7940 localvars = lvp;
7941 INT_ON;
7942}
7943
7944/*
7945 * The "local" command.
7946 */
7947static int
7948localcmd(int argc, char **argv)
7949{
7950 char *name;
7951
7952 argv = argptr;
7953 while ((name = *argv++) != NULL) {
7954 mklocal(name);
7955 }
7956 return 0;
7957}
7958
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00007959static int
7960falsecmd(int argc, char **argv)
7961{
7962 return 1;
7963}
7964
7965static int
7966truecmd(int argc, char **argv)
7967{
7968 return 0;
7969}
7970
7971static int
7972execcmd(int argc, char **argv)
7973{
7974 if (argc > 1) {
7975 iflag = 0; /* exit on error */
7976 mflag = 0;
7977 optschanged();
7978 shellexec(argv + 1, pathval(), 0);
7979 }
7980 return 0;
7981}
7982
7983/*
7984 * The return command.
7985 */
7986static int
7987returncmd(int argc, char **argv)
7988{
7989 /*
7990 * If called outside a function, do what ksh does;
7991 * skip the rest of the file.
7992 */
7993 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
7994 return argv[1] ? number(argv[1]) : exitstatus;
7995}
7996
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007997/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007998static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007999static int dotcmd(int, char **);
8000static int evalcmd(int, char **);
8001#if ENABLE_ASH_BUILTIN_ECHO
8002static int echocmd(int, char **);
8003#endif
8004#if ENABLE_ASH_BUILTIN_TEST
8005static int testcmd(int, char **);
8006#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008007static int exitcmd(int, char **);
8008static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008009#if ENABLE_ASH_GETOPTS
8010static int getoptscmd(int, char **);
8011#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008012#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8013static int helpcmd(int argc, char **argv);
8014#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008015#if ENABLE_ASH_MATH_SUPPORT
8016static int letcmd(int, char **);
8017#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008018static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008019static int setcmd(int, char **);
8020static int shiftcmd(int, char **);
8021static int timescmd(int, char **);
8022static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008023static int umaskcmd(int, char **);
8024static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008025static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008026
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008027#define BUILTIN_NOSPEC "0"
8028#define BUILTIN_SPECIAL "1"
8029#define BUILTIN_REGULAR "2"
8030#define BUILTIN_SPEC_REG "3"
8031#define BUILTIN_ASSIGN "4"
8032#define BUILTIN_SPEC_ASSG "5"
8033#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008034#define BUILTIN_SPEC_REG_ASSG "7"
8035
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008036/* make sure to keep these in proper order since it is searched via bsearch() */
8037static const struct builtincmd builtintab[] = {
8038 { BUILTIN_SPEC_REG ".", dotcmd },
8039 { BUILTIN_SPEC_REG ":", truecmd },
8040#if ENABLE_ASH_BUILTIN_TEST
8041 { BUILTIN_REGULAR "[", testcmd },
8042 { BUILTIN_REGULAR "[[", testcmd },
8043#endif
8044#if ENABLE_ASH_ALIAS
8045 { BUILTIN_REG_ASSG "alias", aliascmd },
8046#endif
8047#if JOBS
8048 { BUILTIN_REGULAR "bg", fg_bgcmd },
8049#endif
8050 { BUILTIN_SPEC_REG "break", breakcmd },
8051 { BUILTIN_REGULAR "cd", cdcmd },
8052 { BUILTIN_NOSPEC "chdir", cdcmd },
8053#if ENABLE_ASH_CMDCMD
8054 { BUILTIN_REGULAR "command", commandcmd },
8055#endif
8056 { BUILTIN_SPEC_REG "continue", breakcmd },
8057#if ENABLE_ASH_BUILTIN_ECHO
8058 { BUILTIN_REGULAR "echo", echocmd },
8059#endif
8060 { BUILTIN_SPEC_REG "eval", evalcmd },
8061 { BUILTIN_SPEC_REG "exec", execcmd },
8062 { BUILTIN_SPEC_REG "exit", exitcmd },
8063 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8064 { BUILTIN_REGULAR "false", falsecmd },
8065#if JOBS
8066 { BUILTIN_REGULAR "fg", fg_bgcmd },
8067#endif
8068#if ENABLE_ASH_GETOPTS
8069 { BUILTIN_REGULAR "getopts", getoptscmd },
8070#endif
8071 { BUILTIN_NOSPEC "hash", hashcmd },
8072#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8073 { BUILTIN_NOSPEC "help", helpcmd },
8074#endif
8075#if JOBS
8076 { BUILTIN_REGULAR "jobs", jobscmd },
8077 { BUILTIN_REGULAR "kill", killcmd },
8078#endif
8079#if ENABLE_ASH_MATH_SUPPORT
8080 { BUILTIN_NOSPEC "let", letcmd },
8081#endif
8082 { BUILTIN_ASSIGN "local", localcmd },
8083 { BUILTIN_NOSPEC "pwd", pwdcmd },
8084 { BUILTIN_REGULAR "read", readcmd },
8085 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8086 { BUILTIN_SPEC_REG "return", returncmd },
8087 { BUILTIN_SPEC_REG "set", setcmd },
8088 { BUILTIN_SPEC_REG "shift", shiftcmd },
8089 { BUILTIN_SPEC_REG "source", dotcmd },
8090#if ENABLE_ASH_BUILTIN_TEST
8091 { BUILTIN_REGULAR "test", testcmd },
8092#endif
8093 { BUILTIN_SPEC_REG "times", timescmd },
8094 { BUILTIN_SPEC_REG "trap", trapcmd },
8095 { BUILTIN_REGULAR "true", truecmd },
8096 { BUILTIN_NOSPEC "type", typecmd },
8097 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8098 { BUILTIN_REGULAR "umask", umaskcmd },
8099#if ENABLE_ASH_ALIAS
8100 { BUILTIN_REGULAR "unalias", unaliascmd },
8101#endif
8102 { BUILTIN_SPEC_REG "unset", unsetcmd },
8103 { BUILTIN_REGULAR "wait", waitcmd },
8104};
8105
8106#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
8107
8108#define COMMANDCMD (builtintab + 5 + \
8109 2 * ENABLE_ASH_BUILTIN_TEST + \
8110 ENABLE_ASH_ALIAS + \
8111 ENABLE_ASH_JOB_CONTROL)
8112#define EXECCMD (builtintab + 7 + \
8113 2 * ENABLE_ASH_BUILTIN_TEST + \
8114 ENABLE_ASH_ALIAS + \
8115 ENABLE_ASH_JOB_CONTROL + \
8116 ENABLE_ASH_CMDCMD + \
8117 ENABLE_ASH_BUILTIN_ECHO)
8118
8119/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008120 * Search the table of builtin commands.
8121 */
8122static struct builtincmd *
8123find_builtin(const char *name)
8124{
8125 struct builtincmd *bp;
8126
8127 bp = bsearch(
8128 name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
8129 pstrcmp
8130 );
8131 return bp;
8132}
8133
8134/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008135 * Execute a simple command.
8136 */
8137static int back_exitstatus; /* exit status of backquoted command */
8138static int
8139isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008140{
8141 const char *q = endofname(p);
8142 if (p == q)
8143 return 0;
8144 return *q == '=';
8145}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008146static int
8147bltincmd(int argc, char **argv)
8148{
8149 /* Preserve exitstatus of a previous possible redirection
8150 * as POSIX mandates */
8151 return back_exitstatus;
8152}
Eric Andersenc470f442003-07-28 09:56:35 +00008153static void
8154evalcommand(union node *cmd, int flags)
8155{
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008156 static const struct builtincmd bltin = {
8157 "\0\0", bltincmd
8158 };
Eric Andersenc470f442003-07-28 09:56:35 +00008159 struct stackmark smark;
8160 union node *argp;
8161 struct arglist arglist;
8162 struct arglist varlist;
8163 char **argv;
8164 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008165 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008166 struct cmdentry cmdentry;
8167 struct job *jp;
8168 char *lastarg;
8169 const char *path;
8170 int spclbltin;
8171 int cmd_is_exec;
8172 int status;
8173 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008174 struct builtincmd *bcmd;
8175 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008176
8177 /* First expand the arguments. */
8178 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8179 setstackmark(&smark);
8180 back_exitstatus = 0;
8181
8182 cmdentry.cmdtype = CMDBUILTIN;
8183 cmdentry.u.cmd = &bltin;
8184 varlist.lastp = &varlist.list;
8185 *varlist.lastp = NULL;
8186 arglist.lastp = &arglist.list;
8187 *arglist.lastp = NULL;
8188
8189 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008190 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008191 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8192 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8193 }
8194
Eric Andersenc470f442003-07-28 09:56:35 +00008195 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8196 struct strlist **spp;
8197
8198 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008199 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008200 expandarg(argp, &arglist, EXP_VARTILDE);
8201 else
8202 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8203
Eric Andersenc470f442003-07-28 09:56:35 +00008204 for (sp = *spp; sp; sp = sp->next)
8205 argc++;
8206 }
8207
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008208 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008209 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008210 TRACE(("evalcommand arg: %s\n", sp->text));
8211 *nargv++ = sp->text;
8212 }
8213 *nargv = NULL;
8214
8215 lastarg = NULL;
8216 if (iflag && funcnest == 0 && argc > 0)
8217 lastarg = nargv[-1];
8218
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008219 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008220 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008221 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008222
8223 path = vpath.text;
8224 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8225 struct strlist **spp;
8226 char *p;
8227
8228 spp = varlist.lastp;
8229 expandarg(argp, &varlist, EXP_VARTILDE);
8230
8231 /*
8232 * Modify the command lookup path, if a PATH= assignment
8233 * is present
8234 */
8235 p = (*spp)->text;
8236 if (varequal(p, path))
8237 path = p;
8238 }
8239
8240 /* Print the command if xflag is set. */
8241 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008242 int n;
8243 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008244
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008245 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008246 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008247
8248 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008249 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008250 while (sp) {
8251 dprintf(preverrout_fd, p, sp->text);
8252 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008253 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008254 p--;
8255 }
8256 }
8257 sp = arglist.list;
8258 }
Rob Landley53437472006-07-16 08:14:35 +00008259 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008260 }
8261
8262 cmd_is_exec = 0;
8263 spclbltin = -1;
8264
8265 /* Now locate the command. */
8266 if (argc) {
8267 const char *oldpath;
8268 int cmd_flag = DO_ERR;
8269
8270 path += 5;
8271 oldpath = path;
8272 for (;;) {
8273 find_command(argv[0], &cmdentry, cmd_flag, path);
8274 if (cmdentry.cmdtype == CMDUNKNOWN) {
8275 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008276 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008277 goto bail;
8278 }
8279
8280 /* implement bltin and command here */
8281 if (cmdentry.cmdtype != CMDBUILTIN)
8282 break;
8283 if (spclbltin < 0)
8284 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8285 if (cmdentry.u.cmd == EXECCMD)
8286 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008287#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008288 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008289 path = oldpath;
8290 nargv = parse_command_args(argv, &path);
8291 if (!nargv)
8292 break;
8293 argc -= nargv - argv;
8294 argv = nargv;
8295 cmd_flag |= DO_NOFUNC;
8296 } else
8297#endif
8298 break;
8299 }
8300 }
8301
8302 if (status) {
8303 /* We have a redirection error. */
8304 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008305 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008306 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008307 exitstatus = status;
8308 goto out;
8309 }
8310
8311 /* Execute the command. */
8312 switch (cmdentry.cmdtype) {
8313 default:
8314 /* Fork off a child process if necessary. */
8315 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008316 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008317 jp = makejob(cmd, 1);
8318 if (forkshell(jp, cmd, FORK_FG) != 0) {
8319 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008320 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008321 break;
8322 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008323 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008324 }
8325 listsetvar(varlist.list, VEXPORT|VSTACK);
8326 shellexec(argv, path, cmdentry.u.index);
8327 /* NOTREACHED */
8328
8329 case CMDBUILTIN:
8330 cmdenviron = varlist.list;
8331 if (cmdenviron) {
8332 struct strlist *list = cmdenviron;
8333 int i = VNOSET;
8334 if (spclbltin > 0 || argc == 0) {
8335 i = 0;
8336 if (cmd_is_exec && argc > 1)
8337 i = VEXPORT;
8338 }
8339 listsetvar(list, i);
8340 }
8341 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8342 int exit_status;
8343 int i, j;
8344
8345 i = exception;
8346 if (i == EXEXIT)
8347 goto raise;
8348
8349 exit_status = 2;
8350 j = 0;
8351 if (i == EXINT)
8352 j = SIGINT;
8353 if (i == EXSIG)
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008354 j = pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008355 if (j)
8356 exit_status = j + 128;
8357 exitstatus = exit_status;
8358
8359 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008360 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008361 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008362 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008363 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008364 }
8365 break;
8366
8367 case CMDFUNCTION:
8368 listsetvar(varlist.list, 0);
8369 if (evalfun(cmdentry.u.func, argc, argv, flags))
8370 goto raise;
8371 break;
8372 }
8373
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008374 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008375 popredir(cmd_is_exec);
8376 if (lastarg)
8377 /* dsl: I think this is intended to be used to support
8378 * '_' in 'vi' command mode during line editing...
8379 * However I implemented that within libedit itself.
8380 */
8381 setvar("_", lastarg, 0);
8382 popstackmark(&smark);
8383}
8384
8385static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008386evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8387{
Eric Andersenc470f442003-07-28 09:56:35 +00008388 char *volatile savecmdname;
8389 struct jmploc *volatile savehandler;
8390 struct jmploc jmploc;
8391 int i;
8392
8393 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008394 i = setjmp(jmploc.loc);
8395 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008396 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008397 savehandler = exception_handler;
8398 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008399 commandname = argv[0];
8400 argptr = argv + 1;
8401 optptr = NULL; /* initialize nextopt */
8402 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008403 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008404 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008405 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008406 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008407 commandname = savecmdname;
8408 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008409 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008410
8411 return i;
8412}
8413
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008414static int
8415goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008416{
8417 return !*endofname(p);
8418}
8419
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008420
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008421/*
8422 * Search for a command. This is called before we fork so that the
8423 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008424 * the child. The check for "goodname" is an overly conservative
8425 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008426 */
Eric Andersenc470f442003-07-28 09:56:35 +00008427static void
8428prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008429{
8430 struct cmdentry entry;
8431
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008432 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8433 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008434}
8435
Eric Andersencb57d552001-06-28 07:25:16 +00008436
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008437/* ============ Builtin commands
8438 *
8439 * Builtin commands whose functions are closely tied to evaluation
8440 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008441 */
8442
8443/*
Eric Andersencb57d552001-06-28 07:25:16 +00008444 * Handle break and continue commands. Break, continue, and return are
8445 * all handled by setting the evalskip flag. The evaluation routines
8446 * above all check this flag, and if it is set they start skipping
8447 * commands rather than executing them. The variable skipcount is
8448 * the number of loops to break/continue, or the number of function
8449 * levels to return. (The latter is always 1.) It should probably
8450 * be an error to break out of more loops than exist, but it isn't
8451 * in the standard shell so we don't make it one here.
8452 */
Eric Andersenc470f442003-07-28 09:56:35 +00008453static int
8454breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008455{
8456 int n = argc > 1 ? number(argv[1]) : 1;
8457
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008458 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008459 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008460 if (n > loopnest)
8461 n = loopnest;
8462 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008463 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008464 skipcount = n;
8465 }
8466 return 0;
8467}
8468
Eric Andersenc470f442003-07-28 09:56:35 +00008469
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008470/* ============ input.c
8471 *
Eric Andersen90898442003-08-06 11:20:52 +00008472 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008473 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008474
Eric Andersenc470f442003-07-28 09:56:35 +00008475#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008476
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008477enum {
8478 INPUT_PUSH_FILE = 1,
8479 INPUT_NOFILE_OK = 2,
8480};
Eric Andersencb57d552001-06-28 07:25:16 +00008481
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008482/*
8483 * NEOF is returned by parsecmd when it encounters an end of file. It
8484 * must be distinct from NULL, so we use the address of a variable that
8485 * happens to be handy.
8486 */
8487static int plinno = 1; /* input line number */
8488/* number of characters left in input buffer */
8489static int parsenleft; /* copy of parsefile->nleft */
8490static int parselleft; /* copy of parsefile->lleft */
8491/* next character in input buffer */
8492static char *parsenextc; /* copy of parsefile->nextc */
8493
8494static int checkkwd;
8495/* values of checkkwd variable */
8496#define CHKALIAS 0x1
8497#define CHKKWD 0x2
8498#define CHKNL 0x4
8499
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008500static void
8501popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008502{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008503 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008504
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008505 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008506#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008507 if (sp->ap) {
8508 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8509 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008510 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008511 if (sp->string != sp->ap->val) {
8512 free(sp->string);
8513 }
8514 sp->ap->flag &= ~ALIASINUSE;
8515 if (sp->ap->flag & ALIASDEAD) {
8516 unalias(sp->ap->name);
8517 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008518 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008519#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008520 parsenextc = sp->prevstring;
8521 parsenleft = sp->prevnleft;
8522/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8523 parsefile->strpush = sp->prev;
8524 if (sp != &(parsefile->basestrpush))
8525 free(sp);
8526 INT_ON;
8527}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008528
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008529static int
8530preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008531{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008532 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008533 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008534 parsenextc = buf;
8535
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008536 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008537#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008538 if (!iflag || parsefile->fd)
8539 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8540 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008541#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008542 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008543#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008544 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8545 if (nr == 0) {
8546 /* Ctrl+C pressed */
8547 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008548 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008549 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008550 raise(SIGINT);
8551 return 1;
8552 }
Eric Andersenc470f442003-07-28 09:56:35 +00008553 goto retry;
8554 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008555 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00008556 /* Ctrl+D presend */
8557 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008558 }
Eric Andersencb57d552001-06-28 07:25:16 +00008559 }
8560#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008561 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008562#endif
8563
8564 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008565 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
8566 int flags = fcntl(0, F_GETFL, 0);
8567 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00008568 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008569 if (fcntl(0, F_SETFL, flags) >= 0) {
8570 out2str("sh: turning off NDELAY mode\n");
8571 goto retry;
8572 }
8573 }
8574 }
8575 }
8576 return nr;
8577}
8578
8579/*
8580 * Refill the input buffer and return the next input character:
8581 *
8582 * 1) If a string was pushed back on the input, pop it;
8583 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8584 * from a string so we can't refill the buffer, return EOF.
8585 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8586 * 4) Process input up to the next newline, deleting nul characters.
8587 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008588static int
Eric Andersenc470f442003-07-28 09:56:35 +00008589preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008590{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008591 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008592 int more;
8593 char savec;
8594
8595 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008596#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008597 if (parsenleft == -1 && parsefile->strpush->ap &&
8598 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008599 return PEOA;
8600 }
Eric Andersen2870d962001-07-02 17:27:21 +00008601#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008602 popstring();
8603 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008604 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008605 }
8606 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8607 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008608 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008609
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008610 more = parselleft;
8611 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008612 again:
8613 more = preadfd();
8614 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008615 parselleft = parsenleft = EOF_NLEFT;
8616 return PEOF;
8617 }
8618 }
8619
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008620 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008621
8622 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008623 for (;;) {
8624 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008625
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008626 more--;
8627 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008628
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008629 if (!c)
8630 memmove(q, q + 1, more);
8631 else {
8632 q++;
8633 if (c == '\n') {
8634 parsenleft = q - parsenextc - 1;
8635 break;
8636 }
Eric Andersencb57d552001-06-28 07:25:16 +00008637 }
8638
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008639 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008640 parsenleft = q - parsenextc - 1;
8641 if (parsenleft < 0)
8642 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008643 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008644 }
8645 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008646 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008647
8648 savec = *q;
8649 *q = '\0';
8650
8651 if (vflag) {
8652 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008653 }
8654
8655 *q = savec;
8656
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008657 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008658}
8659
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008660#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008661static int
8662pgetc(void)
8663{
8664 return pgetc_as_macro();
8665}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008666
8667#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8668#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008669#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008670#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008671#endif
8672
8673/*
8674 * Same as pgetc(), but ignores PEOA.
8675 */
8676#if ENABLE_ASH_ALIAS
8677static int
8678pgetc2(void)
8679{
8680 int c;
8681
8682 do {
8683 c = pgetc_macro();
8684 } while (c == PEOA);
8685 return c;
8686}
8687#else
8688static int
8689pgetc2(void)
8690{
8691 return pgetc_macro();
8692}
8693#endif
8694
8695/*
8696 * Read a line from the script.
8697 */
8698static char *
8699pfgets(char *line, int len)
8700{
8701 char *p = line;
8702 int nleft = len;
8703 int c;
8704
8705 while (--nleft > 0) {
8706 c = pgetc2();
8707 if (c == PEOF) {
8708 if (p == line)
8709 return NULL;
8710 break;
8711 }
8712 *p++ = c;
8713 if (c == '\n')
8714 break;
8715 }
8716 *p = '\0';
8717 return line;
8718}
8719
Eric Andersenc470f442003-07-28 09:56:35 +00008720/*
8721 * Undo the last call to pgetc. Only one character may be pushed back.
8722 * PEOF may be pushed back.
8723 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008724static void
Eric Andersenc470f442003-07-28 09:56:35 +00008725pungetc(void)
8726{
8727 parsenleft++;
8728 parsenextc--;
8729}
Eric Andersencb57d552001-06-28 07:25:16 +00008730
8731/*
8732 * Push a string back onto the input at this current parsefile level.
8733 * We handle aliases this way.
8734 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008735static void
Eric Andersenc470f442003-07-28 09:56:35 +00008736pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008737{
Eric Andersencb57d552001-06-28 07:25:16 +00008738 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008739 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008740
Eric Andersenc470f442003-07-28 09:56:35 +00008741 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008742 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008743/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8744 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008745 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008746 sp->prev = parsefile->strpush;
8747 parsefile->strpush = sp;
8748 } else
8749 sp = parsefile->strpush = &(parsefile->basestrpush);
8750 sp->prevstring = parsenextc;
8751 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008752#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008753 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008754 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008755 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008756 sp->string = s;
8757 }
Eric Andersen2870d962001-07-02 17:27:21 +00008758#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008759 parsenextc = s;
8760 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008761 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008762}
8763
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008764/*
8765 * To handle the "." command, a stack of input files is used. Pushfile
8766 * adds a new entry to the stack and popfile restores the previous level.
8767 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008768static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008769pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008770{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008771 struct parsefile *pf;
8772
8773 parsefile->nleft = parsenleft;
8774 parsefile->lleft = parselleft;
8775 parsefile->nextc = parsenextc;
8776 parsefile->linno = plinno;
8777 pf = ckmalloc(sizeof(*pf));
8778 pf->prev = parsefile;
8779 pf->fd = -1;
8780 pf->strpush = NULL;
8781 pf->basestrpush.prev = NULL;
8782 parsefile = pf;
8783}
8784
8785static void
8786popfile(void)
8787{
8788 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008789
Denis Vlasenkob012b102007-02-19 22:43:01 +00008790 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008791 if (pf->fd >= 0)
8792 close(pf->fd);
8793 if (pf->buf)
8794 free(pf->buf);
8795 while (pf->strpush)
8796 popstring();
8797 parsefile = pf->prev;
8798 free(pf);
8799 parsenleft = parsefile->nleft;
8800 parselleft = parsefile->lleft;
8801 parsenextc = parsefile->nextc;
8802 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008803 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008804}
8805
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008806/*
8807 * Return to top level.
8808 */
8809static void
8810popallfiles(void)
8811{
8812 while (parsefile != &basepf)
8813 popfile();
8814}
8815
8816/*
8817 * Close the file(s) that the shell is reading commands from. Called
8818 * after a fork is done.
8819 */
8820static void
8821closescript(void)
8822{
8823 popallfiles();
8824 if (parsefile->fd > 0) {
8825 close(parsefile->fd);
8826 parsefile->fd = 0;
8827 }
8828}
8829
8830/*
8831 * Like setinputfile, but takes an open file descriptor. Call this with
8832 * interrupts off.
8833 */
8834static void
8835setinputfd(int fd, int push)
8836{
8837 fcntl(fd, F_SETFD, FD_CLOEXEC);
8838 if (push) {
8839 pushfile();
8840 parsefile->buf = 0;
8841 }
8842 parsefile->fd = fd;
8843 if (parsefile->buf == NULL)
8844 parsefile->buf = ckmalloc(IBUFSIZ);
8845 parselleft = parsenleft = 0;
8846 plinno = 1;
8847}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008848
Eric Andersenc470f442003-07-28 09:56:35 +00008849/*
8850 * Set the input to take input from a file. If push is set, push the
8851 * old input onto the stack first.
8852 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008853static int
8854setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008855{
8856 int fd;
8857 int fd2;
8858
Denis Vlasenkob012b102007-02-19 22:43:01 +00008859 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008860 fd = open(fname, O_RDONLY);
8861 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008862 if (flags & INPUT_NOFILE_OK)
8863 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008864 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008865 }
Eric Andersenc470f442003-07-28 09:56:35 +00008866 if (fd < 10) {
8867 fd2 = copyfd(fd, 10);
8868 close(fd);
8869 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008870 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008871 fd = fd2;
8872 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008873 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008874 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008875 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008876 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008877}
8878
Eric Andersencb57d552001-06-28 07:25:16 +00008879/*
8880 * Like setinputfile, but takes input from a string.
8881 */
Eric Andersenc470f442003-07-28 09:56:35 +00008882static void
8883setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00008884{
Denis Vlasenkob012b102007-02-19 22:43:01 +00008885 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008886 pushfile();
8887 parsenextc = string;
8888 parsenleft = strlen(string);
8889 parsefile->buf = NULL;
8890 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008891 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008892}
8893
8894
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008895/* ============ mail.c
8896 *
8897 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00008898 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008899
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008900#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00008901
Eric Andersencb57d552001-06-28 07:25:16 +00008902#define MAXMBOXES 10
8903
Eric Andersenc470f442003-07-28 09:56:35 +00008904/* times of mailboxes */
8905static time_t mailtime[MAXMBOXES];
8906/* Set if MAIL or MAILPATH is changed. */
8907static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008908
Eric Andersencb57d552001-06-28 07:25:16 +00008909/*
Eric Andersenc470f442003-07-28 09:56:35 +00008910 * Print appropriate message(s) if mail has arrived.
8911 * If mail_var_path_changed is set,
8912 * then the value of MAIL has mail_var_path_changed,
8913 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008914 */
Eric Andersenc470f442003-07-28 09:56:35 +00008915static void
8916chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008917{
Eric Andersencb57d552001-06-28 07:25:16 +00008918 const char *mpath;
8919 char *p;
8920 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008921 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008922 struct stackmark smark;
8923 struct stat statb;
8924
Eric Andersencb57d552001-06-28 07:25:16 +00008925 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008926 mpath = mpathset() ? mpathval() : mailval();
8927 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008928 p = padvance(&mpath, nullstr);
8929 if (p == NULL)
8930 break;
8931 if (*p == '\0')
8932 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008933 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008934#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008935 if (q[-1] != '/')
8936 abort();
8937#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008938 q[-1] = '\0'; /* delete trailing '/' */
8939 if (stat(p, &statb) < 0) {
8940 *mtp = 0;
8941 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008942 }
Eric Andersenc470f442003-07-28 09:56:35 +00008943 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8944 fprintf(
8945 stderr, snlfmt,
8946 pathopt ? pathopt : "you have mail"
8947 );
8948 }
8949 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008950 }
Eric Andersenc470f442003-07-28 09:56:35 +00008951 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008952 popstackmark(&smark);
8953}
Eric Andersencb57d552001-06-28 07:25:16 +00008954
Eric Andersenc470f442003-07-28 09:56:35 +00008955static void
8956changemail(const char *val)
8957{
8958 mail_var_path_changed++;
8959}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008960
Denis Vlasenko131ae172007-02-18 13:00:19 +00008961#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008962
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008963
8964/* ============ ??? */
8965
Eric Andersencb57d552001-06-28 07:25:16 +00008966/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008967 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00008968 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008969static void
8970setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008971{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008972 char **newparam;
8973 char **ap;
8974 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00008975
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008976 for (nparam = 0; argv[nparam]; nparam++);
8977 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
8978 while (*argv) {
8979 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008980 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008981 *ap = NULL;
8982 freeparam(&shellparam);
8983 shellparam.malloc = 1;
8984 shellparam.nparam = nparam;
8985 shellparam.p = newparam;
8986#if ENABLE_ASH_GETOPTS
8987 shellparam.optind = 1;
8988 shellparam.optoff = -1;
8989#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008990}
8991
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008992/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008993 * Process shell options. The global variable argptr contains a pointer
8994 * to the argument list; we advance it past the options.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008995 */
8996static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008997minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008998{
8999 int i;
9000
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009001 if (name) {
9002 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009003 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009004 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00009005 return;
9006 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009007 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009008 ash_msg_and_raise_error("illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00009009 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009010 out1str("Current option settings\n");
9011 for (i = 0; i < NOPTS; i++)
9012 out1fmt("%-16s%s\n", optnames(i),
9013 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00009014}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009015static void
9016setoption(int flag, int val)
9017{
9018 int i;
9019
9020 for (i = 0; i < NOPTS; i++) {
9021 if (optletters(i) == flag) {
9022 optlist[i] = val;
9023 return;
9024 }
9025 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009026 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009027 /* NOTREACHED */
9028}
Eric Andersenc470f442003-07-28 09:56:35 +00009029static void
9030options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009031{
9032 char *p;
9033 int val;
9034 int c;
9035
9036 if (cmdline)
9037 minusc = NULL;
9038 while ((p = *argptr) != NULL) {
9039 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009040 c = *p++;
9041 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009042 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009043 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009044 if (!cmdline) {
9045 /* "-" means turn off -x and -v */
9046 if (p[0] == '\0')
9047 xflag = vflag = 0;
9048 /* "--" means reset params */
9049 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009050 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009051 }
Eric Andersenc470f442003-07-28 09:56:35 +00009052 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009053 }
9054 } else if (c == '+') {
9055 val = 0;
9056 } else {
9057 argptr--;
9058 break;
9059 }
9060 while ((c = *p++) != '\0') {
9061 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00009062 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00009063 } else if (c == 'o') {
9064 minus_o(*argptr, val);
9065 if (*argptr)
9066 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00009067 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009068 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009069 isloginsh = 1;
9070 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009071 } else {
9072 setoption(c, val);
9073 }
9074 }
9075 }
9076}
9077
Eric Andersencb57d552001-06-28 07:25:16 +00009078/*
Eric Andersencb57d552001-06-28 07:25:16 +00009079 * The shift builtin command.
9080 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009081static int
Eric Andersenc470f442003-07-28 09:56:35 +00009082shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009083{
9084 int n;
9085 char **ap1, **ap2;
9086
9087 n = 1;
9088 if (argc > 1)
9089 n = number(argv[1]);
9090 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009091 ash_msg_and_raise_error("can't shift that many");
9092 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009093 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009094 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009095 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009096 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009097 }
9098 ap2 = shellparam.p;
9099 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009100#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009101 shellparam.optind = 1;
9102 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009103#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009104 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009105 return 0;
9106}
9107
Eric Andersencb57d552001-06-28 07:25:16 +00009108/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009109 * POSIX requires that 'set' (but not export or readonly) output the
9110 * variables in lexicographic order - by the locale's collating order (sigh).
9111 * Maybe we could keep them in an ordered balanced binary tree
9112 * instead of hashed lists.
9113 * For now just roll 'em through qsort for printing...
9114 */
9115static int
9116showvars(const char *sep_prefix, int on, int off)
9117{
9118 const char *sep;
9119 char **ep, **epend;
9120
9121 ep = listvars(on, off, &epend);
9122 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9123
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009124 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009125
9126 for (; ep < epend; ep++) {
9127 const char *p;
9128 const char *q;
9129
9130 p = strchrnul(*ep, '=');
9131 q = nullstr;
9132 if (*p)
9133 q = single_quote(++p);
9134 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9135 }
9136 return 0;
9137}
9138
9139/*
Eric Andersencb57d552001-06-28 07:25:16 +00009140 * The set command builtin.
9141 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009142static int
Eric Andersenc470f442003-07-28 09:56:35 +00009143setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009144{
9145 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009146 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009147 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009148 options(0);
9149 optschanged();
9150 if (*argptr != NULL) {
9151 setparam(argptr);
9152 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009153 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009154 return 0;
9155}
9156
Denis Vlasenko131ae172007-02-18 13:00:19 +00009157#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009158/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009159static void
9160change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009161{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009162 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009163 /* "get", generate */
9164 char buf[16];
9165
9166 rseed = rseed * 1103515245 + 12345;
9167 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9168 /* set without recursion */
9169 setvar(vrandom.text, buf, VNOFUNC);
9170 vrandom.flags &= ~VNOFUNC;
9171 } else {
9172 /* set/reset */
9173 rseed = strtoul(value, (char **)NULL, 10);
9174 }
Eric Andersenef02f822004-03-11 13:34:24 +00009175}
Eric Andersen16767e22004-03-16 05:14:10 +00009176#endif
9177
Denis Vlasenko131ae172007-02-18 13:00:19 +00009178#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009179static int
Eric Andersenc470f442003-07-28 09:56:35 +00009180getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009181{
9182 char *p, *q;
9183 char c = '?';
9184 int done = 0;
9185 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009186 char s[12];
9187 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009188
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009189 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009190 return 1;
9191 optnext = optfirst + *param_optind - 1;
9192
9193 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009194 p = NULL;
9195 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009196 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009197 if (p == NULL || *p == '\0') {
9198 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009199 p = *optnext;
9200 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009201 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009202 p = NULL;
9203 done = 1;
9204 goto out;
9205 }
9206 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009207 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009208 goto atend;
9209 }
9210
9211 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009212 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009213 if (*q == '\0') {
9214 if (optstr[0] == ':') {
9215 s[0] = c;
9216 s[1] = '\0';
9217 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009218 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009219 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009220 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009221 }
9222 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009223 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009224 }
9225 if (*++q == ':')
9226 q++;
9227 }
9228
9229 if (*++q == ':') {
9230 if (*p == '\0' && (p = *optnext) == NULL) {
9231 if (optstr[0] == ':') {
9232 s[0] = c;
9233 s[1] = '\0';
9234 err |= setvarsafe("OPTARG", s, 0);
9235 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009236 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009237 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009238 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009239 c = '?';
9240 }
Eric Andersenc470f442003-07-28 09:56:35 +00009241 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009242 }
9243
9244 if (p == *optnext)
9245 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009246 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009247 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009248 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009249 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009250 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009251 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009252 *param_optind = optnext - optfirst + 1;
9253 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009254 err |= setvarsafe("OPTIND", s, VNOFUNC);
9255 s[0] = c;
9256 s[1] = '\0';
9257 err |= setvarsafe(optvar, s, 0);
9258 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009259 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009260 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009261 flush_stdout_stderr();
9262 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009263 }
9264 return done;
9265}
Eric Andersenc470f442003-07-28 09:56:35 +00009266
9267/*
9268 * The getopts builtin. Shellparam.optnext points to the next argument
9269 * to be processed. Shellparam.optptr points to the next character to
9270 * be processed in the current argument. If shellparam.optnext is NULL,
9271 * then it's the first time getopts has been called.
9272 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009273static int
Eric Andersenc470f442003-07-28 09:56:35 +00009274getoptscmd(int argc, char **argv)
9275{
9276 char **optbase;
9277
9278 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009279 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009280 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009281 optbase = shellparam.p;
9282 if (shellparam.optind > shellparam.nparam + 1) {
9283 shellparam.optind = 1;
9284 shellparam.optoff = -1;
9285 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009286 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009287 optbase = &argv[3];
9288 if (shellparam.optind > argc - 2) {
9289 shellparam.optind = 1;
9290 shellparam.optoff = -1;
9291 }
9292 }
9293
9294 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009295 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009296}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009297#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009298
Eric Andersencb57d552001-06-28 07:25:16 +00009299
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009300/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009301
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009302static int tokpushback; /* last token pushed back */
9303#define NEOF ((union node *)&tokpushback)
9304static int parsebackquote; /* nonzero if we are inside backquotes */
9305static int lasttoken; /* last token read */
9306static char *wordtext; /* text of last word returned by readtoken */
9307static struct nodelist *backquotelist;
9308static union node *redirnode;
9309static struct heredoc *heredoc;
9310static int quoteflag; /* set if (part of) last token was quoted */
9311
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009312static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9313static void
9314raise_error_syntax(const char *msg)
9315{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009316 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009317 /* NOTREACHED */
9318}
9319
9320/*
9321 * Called when an unexpected token is read during the parse. The argument
9322 * is the token that is expected, or -1 if more than one type of token can
9323 * occur at this point.
9324 */
9325static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9326static void
9327raise_error_unexpected_syntax(int token)
9328{
9329 char msg[64];
9330 int l;
9331
9332 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9333 if (token >= 0)
9334 sprintf(msg + l, " (expecting %s)", tokname(token));
9335 raise_error_syntax(msg);
9336 /* NOTREACHED */
9337}
Eric Andersencb57d552001-06-28 07:25:16 +00009338
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009339#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009340
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009341struct heredoc {
9342 struct heredoc *next; /* next here document in list */
9343 union node *here; /* redirection node */
9344 char *eofmark; /* string indicating end of input */
9345 int striptabs; /* if set, strip leading tabs */
9346};
Eric Andersencb57d552001-06-28 07:25:16 +00009347
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009348static struct heredoc *heredoclist; /* list of here documents to read */
9349
9350/* parsing is heavily cross-recursive, need these forward decls */
9351static union node *andor(void);
9352static union node *pipeline(void);
9353static union node *parse_command(void);
9354static void parseheredoc(void);
9355static char peektoken(void);
9356static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009357
Eric Andersenc470f442003-07-28 09:56:35 +00009358static union node *
9359list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009360{
9361 union node *n1, *n2, *n3;
9362 int tok;
9363
Eric Andersenc470f442003-07-28 09:56:35 +00009364 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9365 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009366 return NULL;
9367 n1 = NULL;
9368 for (;;) {
9369 n2 = andor();
9370 tok = readtoken();
9371 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009372 if (n2->type == NPIPE) {
9373 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009374 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009375 if (n2->type != NREDIR) {
9376 n3 = stalloc(sizeof(struct nredir));
9377 n3->nredir.n = n2;
9378 n3->nredir.redirect = NULL;
9379 n2 = n3;
9380 }
9381 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009382 }
9383 }
9384 if (n1 == NULL) {
9385 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009386 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009387 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009388 n3->type = NSEMI;
9389 n3->nbinary.ch1 = n1;
9390 n3->nbinary.ch2 = n2;
9391 n1 = n3;
9392 }
9393 switch (tok) {
9394 case TBACKGND:
9395 case TSEMI:
9396 tok = readtoken();
9397 /* fall through */
9398 case TNL:
9399 if (tok == TNL) {
9400 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009401 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009402 return n1;
9403 } else {
9404 tokpushback++;
9405 }
Eric Andersenc470f442003-07-28 09:56:35 +00009406 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009407 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009408 return n1;
9409 break;
9410 case TEOF:
9411 if (heredoclist)
9412 parseheredoc();
9413 else
Eric Andersenc470f442003-07-28 09:56:35 +00009414 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009415 return n1;
9416 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009417 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009418 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009419 tokpushback++;
9420 return n1;
9421 }
9422 }
9423}
9424
Eric Andersenc470f442003-07-28 09:56:35 +00009425static union node *
9426andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009427{
Eric Andersencb57d552001-06-28 07:25:16 +00009428 union node *n1, *n2, *n3;
9429 int t;
9430
Eric Andersencb57d552001-06-28 07:25:16 +00009431 n1 = pipeline();
9432 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009433 t = readtoken();
9434 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009435 t = NAND;
9436 } else if (t == TOR) {
9437 t = NOR;
9438 } else {
9439 tokpushback++;
9440 return n1;
9441 }
Eric Andersenc470f442003-07-28 09:56:35 +00009442 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009443 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009444 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009445 n3->type = t;
9446 n3->nbinary.ch1 = n1;
9447 n3->nbinary.ch2 = n2;
9448 n1 = n3;
9449 }
9450}
9451
Eric Andersenc470f442003-07-28 09:56:35 +00009452static union node *
9453pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009454{
Eric Andersencb57d552001-06-28 07:25:16 +00009455 union node *n1, *n2, *pipenode;
9456 struct nodelist *lp, *prev;
9457 int negate;
9458
9459 negate = 0;
9460 TRACE(("pipeline: entered\n"));
9461 if (readtoken() == TNOT) {
9462 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009463 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009464 } else
9465 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009466 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009467 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009468 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009469 pipenode->type = NPIPE;
9470 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009471 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009472 pipenode->npipe.cmdlist = lp;
9473 lp->n = n1;
9474 do {
9475 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009476 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009477 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009478 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009479 prev->next = lp;
9480 } while (readtoken() == TPIPE);
9481 lp->next = NULL;
9482 n1 = pipenode;
9483 }
9484 tokpushback++;
9485 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009486 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009487 n2->type = NNOT;
9488 n2->nnot.com = n1;
9489 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009490 }
9491 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009492}
9493
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009494static union node *
9495makename(void)
9496{
9497 union node *n;
9498
9499 n = stalloc(sizeof(struct narg));
9500 n->type = NARG;
9501 n->narg.next = NULL;
9502 n->narg.text = wordtext;
9503 n->narg.backquote = backquotelist;
9504 return n;
9505}
9506
9507static void
9508fixredir(union node *n, const char *text, int err)
9509{
9510 TRACE(("Fix redir %s %d\n", text, err));
9511 if (!err)
9512 n->ndup.vname = NULL;
9513
9514 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009515 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009516 else if (LONE_DASH(text))
9517 n->ndup.dupfd = -1;
9518 else {
9519 if (err)
9520 raise_error_syntax("Bad fd number");
9521 n->ndup.vname = makename();
9522 }
9523}
9524
9525/*
9526 * Returns true if the text contains nothing to expand (no dollar signs
9527 * or backquotes).
9528 */
9529static int
9530noexpand(char *text)
9531{
9532 char *p;
9533 char c;
9534
9535 p = text;
9536 while ((c = *p++) != '\0') {
9537 if (c == CTLQUOTEMARK)
9538 continue;
9539 if (c == CTLESC)
9540 p++;
9541 else if (SIT(c, BASESYNTAX) == CCTL)
9542 return 0;
9543 }
9544 return 1;
9545}
9546
9547static void
9548parsefname(void)
9549{
9550 union node *n = redirnode;
9551
9552 if (readtoken() != TWORD)
9553 raise_error_unexpected_syntax(-1);
9554 if (n->type == NHERE) {
9555 struct heredoc *here = heredoc;
9556 struct heredoc *p;
9557 int i;
9558
9559 if (quoteflag == 0)
9560 n->type = NXHERE;
9561 TRACE(("Here document %d\n", n->type));
9562 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9563 raise_error_syntax("Illegal eof marker for << redirection");
9564 rmescapes(wordtext);
9565 here->eofmark = wordtext;
9566 here->next = NULL;
9567 if (heredoclist == NULL)
9568 heredoclist = here;
9569 else {
9570 for (p = heredoclist; p->next; p = p->next);
9571 p->next = here;
9572 }
9573 } else if (n->type == NTOFD || n->type == NFROMFD) {
9574 fixredir(n, wordtext, 0);
9575 } else {
9576 n->nfile.fname = makename();
9577 }
9578}
Eric Andersencb57d552001-06-28 07:25:16 +00009579
Eric Andersenc470f442003-07-28 09:56:35 +00009580static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009581simplecmd(void)
9582{
9583 union node *args, **app;
9584 union node *n = NULL;
9585 union node *vars, **vpp;
9586 union node **rpp, *redir;
9587 int savecheckkwd;
9588
9589 args = NULL;
9590 app = &args;
9591 vars = NULL;
9592 vpp = &vars;
9593 redir = NULL;
9594 rpp = &redir;
9595
9596 savecheckkwd = CHKALIAS;
9597 for (;;) {
9598 checkkwd = savecheckkwd;
9599 switch (readtoken()) {
9600 case TWORD:
9601 n = stalloc(sizeof(struct narg));
9602 n->type = NARG;
9603 n->narg.text = wordtext;
9604 n->narg.backquote = backquotelist;
9605 if (savecheckkwd && isassignment(wordtext)) {
9606 *vpp = n;
9607 vpp = &n->narg.next;
9608 } else {
9609 *app = n;
9610 app = &n->narg.next;
9611 savecheckkwd = 0;
9612 }
9613 break;
9614 case TREDIR:
9615 *rpp = n = redirnode;
9616 rpp = &n->nfile.next;
9617 parsefname(); /* read name of redirection file */
9618 break;
9619 case TLP:
9620 if (args && app == &args->narg.next
9621 && !vars && !redir
9622 ) {
9623 struct builtincmd *bcmd;
9624 const char *name;
9625
9626 /* We have a function */
9627 if (readtoken() != TRP)
9628 raise_error_unexpected_syntax(TRP);
9629 name = n->narg.text;
9630 if (!goodname(name)
9631 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9632 ) {
9633 raise_error_syntax("Bad function name");
9634 }
9635 n->type = NDEFUN;
9636 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9637 n->narg.next = parse_command();
9638 return n;
9639 }
9640 /* fall through */
9641 default:
9642 tokpushback++;
9643 goto out;
9644 }
9645 }
9646 out:
9647 *app = NULL;
9648 *vpp = NULL;
9649 *rpp = NULL;
9650 n = stalloc(sizeof(struct ncmd));
9651 n->type = NCMD;
9652 n->ncmd.args = args;
9653 n->ncmd.assign = vars;
9654 n->ncmd.redirect = redir;
9655 return n;
9656}
9657
9658static union node *
9659parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009660{
Eric Andersencb57d552001-06-28 07:25:16 +00009661 union node *n1, *n2;
9662 union node *ap, **app;
9663 union node *cp, **cpp;
9664 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009665 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009666 int t;
9667
9668 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009669 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009670
Eric Andersencb57d552001-06-28 07:25:16 +00009671 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009672 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009673 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009674 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009675 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009676 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009677 n1->type = NIF;
9678 n1->nif.test = list(0);
9679 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009680 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009681 n1->nif.ifpart = list(0);
9682 n2 = n1;
9683 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009684 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009685 n2 = n2->nif.elsepart;
9686 n2->type = NIF;
9687 n2->nif.test = list(0);
9688 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009689 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009690 n2->nif.ifpart = list(0);
9691 }
9692 if (lasttoken == TELSE)
9693 n2->nif.elsepart = list(0);
9694 else {
9695 n2->nif.elsepart = NULL;
9696 tokpushback++;
9697 }
Eric Andersenc470f442003-07-28 09:56:35 +00009698 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009699 break;
9700 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009701 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009702 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009703 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009704 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009705 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009706 got = readtoken();
9707 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009708 TRACE(("expecting DO got %s %s\n", tokname(got),
9709 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009710 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009711 }
9712 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009713 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009714 break;
9715 }
9716 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009717 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009718 raise_error_syntax("Bad for loop variable");
9719 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009720 n1->type = NFOR;
9721 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009722 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009723 if (readtoken() == TIN) {
9724 app = &ap;
9725 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009726 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009727 n2->type = NARG;
9728 n2->narg.text = wordtext;
9729 n2->narg.backquote = backquotelist;
9730 *app = n2;
9731 app = &n2->narg.next;
9732 }
9733 *app = NULL;
9734 n1->nfor.args = ap;
9735 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009736 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009737 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009738 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009739 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009740 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009741 n2->narg.backquote = NULL;
9742 n2->narg.next = NULL;
9743 n1->nfor.args = n2;
9744 /*
9745 * Newline or semicolon here is optional (but note
9746 * that the original Bourne shell only allowed NL).
9747 */
9748 if (lasttoken != TNL && lasttoken != TSEMI)
9749 tokpushback++;
9750 }
Eric Andersenc470f442003-07-28 09:56:35 +00009751 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009752 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009753 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009754 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009755 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009756 break;
9757 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009758 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009759 n1->type = NCASE;
9760 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009761 raise_error_unexpected_syntax(TWORD);
9762 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009763 n2->type = NARG;
9764 n2->narg.text = wordtext;
9765 n2->narg.backquote = backquotelist;
9766 n2->narg.next = NULL;
9767 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009768 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009769 } while (readtoken() == TNL);
9770 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009771 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009772 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009773 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009774 checkkwd = CHKNL | CHKKWD;
9775 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009776 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009777 if (lasttoken == TLP)
9778 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009779 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009780 cp->type = NCLIST;
9781 app = &cp->nclist.pattern;
9782 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009783 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009784 ap->type = NARG;
9785 ap->narg.text = wordtext;
9786 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009787 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009788 break;
9789 app = &ap->narg.next;
9790 readtoken();
9791 }
9792 ap->narg.next = NULL;
9793 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009794 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009795 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009796
Eric Andersenc470f442003-07-28 09:56:35 +00009797 cpp = &cp->nclist.next;
9798
9799 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009800 t = readtoken();
9801 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009802 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009803 raise_error_unexpected_syntax(TENDCASE);
9804 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009805 }
Eric Andersenc470f442003-07-28 09:56:35 +00009806 }
Eric Andersencb57d552001-06-28 07:25:16 +00009807 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009808 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009809 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009810 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009811 n1->type = NSUBSHELL;
9812 n1->nredir.n = list(0);
9813 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009814 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009815 break;
9816 case TBEGIN:
9817 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009818 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009819 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009820 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009821 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009822 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009823 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009824 }
9825
Eric Andersenc470f442003-07-28 09:56:35 +00009826 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009827 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009828
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009829 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009830 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009831 checkkwd = CHKKWD | CHKALIAS;
9832 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009833 while (readtoken() == TREDIR) {
9834 *rpp = n2 = redirnode;
9835 rpp = &n2->nfile.next;
9836 parsefname();
9837 }
9838 tokpushback++;
9839 *rpp = NULL;
9840 if (redir) {
9841 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009842 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009843 n2->type = NREDIR;
9844 n2->nredir.n = n1;
9845 n1 = n2;
9846 }
9847 n1->nredir.redirect = redir;
9848 }
Eric Andersencb57d552001-06-28 07:25:16 +00009849 return n1;
9850}
9851
Eric Andersencb57d552001-06-28 07:25:16 +00009852/*
9853 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9854 * is not NULL, read a here document. In the latter case, eofmark is the
9855 * word which marks the end of the document and striptabs is true if
9856 * leading tabs should be stripped from the document. The argument firstc
9857 * is the first character of the input token or document.
9858 *
9859 * Because C does not have internal subroutines, I have simulated them
9860 * using goto's to implement the subroutine linkage. The following macros
9861 * will run code that appears at the end of readtoken1.
9862 */
9863
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009864static int parsebackquote; /* nonzero if we are inside backquotes */
9865
Eric Andersen2870d962001-07-02 17:27:21 +00009866#define CHECKEND() {goto checkend; checkend_return:;}
9867#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9868#define PARSESUB() {goto parsesub; parsesub_return:;}
9869#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9870#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9871#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009872
9873static int
Eric Andersenc470f442003-07-28 09:56:35 +00009874readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009875{
Eric Andersencb57d552001-06-28 07:25:16 +00009876 int c = firstc;
9877 char *out;
9878 int len;
9879 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009880 struct nodelist *bqlist = 0;
9881 int quotef = 0;
9882 int dblquote = 0;
9883 int varnest = 0; /* levels of variables expansion */
9884 int arinest = 0; /* levels of arithmetic expansion */
9885 int parenlevel = 0; /* levels of parens in arithmetic */
9886 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9887 int oldstyle = 0;
9888 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009889#if __GNUC__
9890 /* Avoid longjmp clobbering */
9891 (void) &out;
9892 (void) &quotef;
9893 (void) &dblquote;
9894 (void) &varnest;
9895 (void) &arinest;
9896 (void) &parenlevel;
9897 (void) &dqvarnest;
9898 (void) &oldstyle;
9899 (void) &prevsyntax;
9900 (void) &syntax;
9901#endif
9902
9903 startlinno = plinno;
9904 dblquote = 0;
9905 if (syntax == DQSYNTAX)
9906 dblquote = 1;
9907 quotef = 0;
9908 bqlist = NULL;
9909 varnest = 0;
9910 arinest = 0;
9911 parenlevel = 0;
9912 dqvarnest = 0;
9913
9914 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009915 loop: { /* for each line, until end of word */
9916 CHECKEND(); /* set c to PEOF if at end of here document */
9917 for (;;) { /* until end of line or end of word */
9918 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009919 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009920 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009921 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009922 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009923 USTPUTC(c, out);
9924 plinno++;
9925 if (doprompt)
9926 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009927 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009928 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009929 case CWORD:
9930 USTPUTC(c, out);
9931 break;
9932 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009933 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009934 USTPUTC(CTLESC, out);
9935 USTPUTC(c, out);
9936 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009937 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009938 c = pgetc2();
9939 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009940 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009941 USTPUTC('\\', out);
9942 pungetc();
9943 } else if (c == '\n') {
9944 if (doprompt)
9945 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009946 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009947 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009948 c != '\\' && c != '`' &&
9949 c != '$' && (
9950 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009951 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009952 ) {
9953 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009954 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009955 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009956 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009957 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009958 USTPUTC(c, out);
9959 quotef++;
9960 }
9961 break;
9962 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009963 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009964 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009965 if (eofmark == NULL) {
9966 USTPUTC(CTLQUOTEMARK, out);
9967 }
Eric Andersencb57d552001-06-28 07:25:16 +00009968 break;
9969 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009970 syntax = DQSYNTAX;
9971 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009972 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009973 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009974 if (eofmark != NULL && arinest == 0
9975 && varnest == 0
9976 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009977 USTPUTC(c, out);
9978 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009979 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009980 syntax = BASESYNTAX;
9981 dblquote = 0;
9982 }
9983 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +00009984 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009985 }
9986 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009987 case CVAR: /* '$' */
9988 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +00009989 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009990 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +00009991 if (varnest > 0) {
9992 varnest--;
9993 if (dqvarnest > 0) {
9994 dqvarnest--;
9995 }
9996 USTPUTC(CTLENDVAR, out);
9997 } else {
9998 USTPUTC(c, out);
9999 }
10000 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010001#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010002 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010003 parenlevel++;
10004 USTPUTC(c, out);
10005 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010006 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010007 if (parenlevel > 0) {
10008 USTPUTC(c, out);
10009 --parenlevel;
10010 } else {
10011 if (pgetc() == ')') {
10012 if (--arinest == 0) {
10013 USTPUTC(CTLENDARI, out);
10014 syntax = prevsyntax;
10015 if (syntax == DQSYNTAX)
10016 dblquote = 1;
10017 else
10018 dblquote = 0;
10019 } else
10020 USTPUTC(')', out);
10021 } else {
10022 /*
10023 * unbalanced parens
10024 * (don't 2nd guess - no error)
10025 */
10026 pungetc();
10027 USTPUTC(')', out);
10028 }
10029 }
10030 break;
10031#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010032 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010033 PARSEBACKQOLD();
10034 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010035 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010036 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010037 case CIGN:
10038 break;
10039 default:
10040 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010041 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010042#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010043 if (c != PEOA)
10044#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010045 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010046
Eric Andersencb57d552001-06-28 07:25:16 +000010047 }
10048 c = pgetc_macro();
10049 }
10050 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010051 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010052#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010053 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010054 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010055#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010056 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010057 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010058 if (varnest != 0) {
10059 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010060 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010061 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010062 }
10063 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010064 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010065 out = stackblock();
10066 if (eofmark == NULL) {
10067 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010068 && quotef == 0
10069 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010070 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010071 PARSEREDIR();
10072 return lasttoken = TREDIR;
10073 } else {
10074 pungetc();
10075 }
10076 }
10077 quoteflag = quotef;
10078 backquotelist = bqlist;
10079 grabstackblock(len);
10080 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010081 lasttoken = TWORD;
10082 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010083/* end of readtoken routine */
10084
Eric Andersencb57d552001-06-28 07:25:16 +000010085/*
10086 * Check to see whether we are at the end of the here document. When this
10087 * is called, c is set to the first character of the next input line. If
10088 * we are at the end of the here document, this routine sets the c to PEOF.
10089 */
Eric Andersenc470f442003-07-28 09:56:35 +000010090checkend: {
10091 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010092#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010093 if (c == PEOA) {
10094 c = pgetc2();
10095 }
10096#endif
10097 if (striptabs) {
10098 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010099 c = pgetc2();
10100 }
Eric Andersenc470f442003-07-28 09:56:35 +000010101 }
10102 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010103 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010104 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010105
Eric Andersenc470f442003-07-28 09:56:35 +000010106 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010107 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010108 if (*p == '\n' && *q == '\0') {
10109 c = PEOF;
10110 plinno++;
10111 needprompt = doprompt;
10112 } else {
10113 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010114 }
10115 }
10116 }
10117 }
Eric Andersenc470f442003-07-28 09:56:35 +000010118 goto checkend_return;
10119}
Eric Andersencb57d552001-06-28 07:25:16 +000010120
Eric Andersencb57d552001-06-28 07:25:16 +000010121/*
10122 * Parse a redirection operator. The variable "out" points to a string
10123 * specifying the fd to be redirected. The variable "c" contains the
10124 * first character of the redirection operator.
10125 */
Eric Andersenc470f442003-07-28 09:56:35 +000010126parseredir: {
10127 char fd = *out;
10128 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010129
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010130 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010131 if (c == '>') {
10132 np->nfile.fd = 1;
10133 c = pgetc();
10134 if (c == '>')
10135 np->type = NAPPEND;
10136 else if (c == '|')
10137 np->type = NCLOBBER;
10138 else if (c == '&')
10139 np->type = NTOFD;
10140 else {
10141 np->type = NTO;
10142 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010143 }
Eric Andersenc470f442003-07-28 09:56:35 +000010144 } else { /* c == '<' */
10145 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010146 c = pgetc();
10147 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010148 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010149 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010150 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010151 np->nfile.fd = 0;
10152 }
10153 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010154 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010155 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010156 c = pgetc();
10157 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010158 heredoc->striptabs = 1;
10159 } else {
10160 heredoc->striptabs = 0;
10161 pungetc();
10162 }
10163 break;
10164
10165 case '&':
10166 np->type = NFROMFD;
10167 break;
10168
10169 case '>':
10170 np->type = NFROMTO;
10171 break;
10172
10173 default:
10174 np->type = NFROM;
10175 pungetc();
10176 break;
10177 }
Eric Andersencb57d552001-06-28 07:25:16 +000010178 }
Eric Andersenc470f442003-07-28 09:56:35 +000010179 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010180 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010181 redirnode = np;
10182 goto parseredir_return;
10183}
Eric Andersencb57d552001-06-28 07:25:16 +000010184
Eric Andersencb57d552001-06-28 07:25:16 +000010185/*
10186 * Parse a substitution. At this point, we have read the dollar sign
10187 * and nothing else.
10188 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010189
10190/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10191 * (assuming ascii char codes, as the original implementation did) */
10192#define is_special(c) \
10193 ((((unsigned int)c) - 33 < 32) \
10194 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010195parsesub: {
10196 int subtype;
10197 int typeloc;
10198 int flags;
10199 char *p;
10200 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010201
Eric Andersenc470f442003-07-28 09:56:35 +000010202 c = pgetc();
10203 if (
10204 c <= PEOA_OR_PEOF ||
10205 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10206 ) {
10207 USTPUTC('$', out);
10208 pungetc();
10209 } else if (c == '(') { /* $(command) or $((arith)) */
10210 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010211#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010212 PARSEARITH();
10213#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010214 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010215#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010216 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010217 pungetc();
10218 PARSEBACKQNEW();
10219 }
10220 } else {
10221 USTPUTC(CTLVAR, out);
10222 typeloc = out - (char *)stackblock();
10223 USTPUTC(VSNORMAL, out);
10224 subtype = VSNORMAL;
10225 if (c == '{') {
10226 c = pgetc();
10227 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010228 c = pgetc();
10229 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010230 c = '#';
10231 else
10232 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010233 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010234 subtype = 0;
10235 }
10236 if (c > PEOA_OR_PEOF && is_name(c)) {
10237 do {
10238 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010239 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010240 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010241 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010242 do {
10243 STPUTC(c, out);
10244 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010245 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010246 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010247 USTPUTC(c, out);
10248 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010249 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010250 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010251
Eric Andersenc470f442003-07-28 09:56:35 +000010252 STPUTC('=', out);
10253 flags = 0;
10254 if (subtype == 0) {
10255 switch (c) {
10256 case ':':
10257 flags = VSNUL;
10258 c = pgetc();
10259 /*FALLTHROUGH*/
10260 default:
10261 p = strchr(types, c);
10262 if (p == NULL)
10263 goto badsub;
10264 subtype = p - types + VSNORMAL;
10265 break;
10266 case '%':
10267 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010268 {
10269 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010270 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010271 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010272 c = pgetc();
10273 if (c == cc)
10274 subtype++;
10275 else
10276 pungetc();
10277 break;
10278 }
10279 }
Eric Andersenc470f442003-07-28 09:56:35 +000010280 } else {
10281 pungetc();
10282 }
10283 if (dblquote || arinest)
10284 flags |= VSQUOTE;
10285 *((char *)stackblock() + typeloc) = subtype | flags;
10286 if (subtype != VSNORMAL) {
10287 varnest++;
10288 if (dblquote || arinest) {
10289 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010290 }
10291 }
10292 }
Eric Andersenc470f442003-07-28 09:56:35 +000010293 goto parsesub_return;
10294}
Eric Andersencb57d552001-06-28 07:25:16 +000010295
Eric Andersencb57d552001-06-28 07:25:16 +000010296/*
10297 * Called to parse command substitutions. Newstyle is set if the command
10298 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10299 * list of commands (passed by reference), and savelen is the number of
10300 * characters on the top of the stack which must be preserved.
10301 */
Eric Andersenc470f442003-07-28 09:56:35 +000010302parsebackq: {
10303 struct nodelist **nlpp;
10304 int savepbq;
10305 union node *n;
10306 char *volatile str;
10307 struct jmploc jmploc;
10308 struct jmploc *volatile savehandler;
10309 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010310 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010311#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010312 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010313#endif
10314
Eric Andersenc470f442003-07-28 09:56:35 +000010315 savepbq = parsebackquote;
10316 if (setjmp(jmploc.loc)) {
10317 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010318 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010319 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010320 exception_handler = savehandler;
10321 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010322 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010323 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010324 str = NULL;
10325 savelen = out - (char *)stackblock();
10326 if (savelen > 0) {
10327 str = ckmalloc(savelen);
10328 memcpy(str, stackblock(), savelen);
10329 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010330 savehandler = exception_handler;
10331 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010332 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010333 if (oldstyle) {
10334 /* We must read until the closing backquote, giving special
10335 treatment to some slashes, and then push the string and
10336 reread it as input, interpreting it normally. */
10337 char *pout;
10338 int pc;
10339 size_t psavelen;
10340 char *pstr;
10341
10342
10343 STARTSTACKSTR(pout);
10344 for (;;) {
10345 if (needprompt) {
10346 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010347 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010348 pc = pgetc();
10349 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010350 case '`':
10351 goto done;
10352
10353 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010354 pc = pgetc();
10355 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010356 plinno++;
10357 if (doprompt)
10358 setprompt(2);
10359 /*
10360 * If eating a newline, avoid putting
10361 * the newline into the new character
10362 * stream (via the STPUTC after the
10363 * switch).
10364 */
10365 continue;
10366 }
10367 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010368 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010369 STPUTC('\\', pout);
10370 if (pc > PEOA_OR_PEOF) {
10371 break;
10372 }
10373 /* fall through */
10374
10375 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010376#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010377 case PEOA:
10378#endif
10379 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010380 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010381
10382 case '\n':
10383 plinno++;
10384 needprompt = doprompt;
10385 break;
10386
10387 default:
10388 break;
10389 }
10390 STPUTC(pc, pout);
10391 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010392 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010393 STPUTC('\0', pout);
10394 psavelen = pout - (char *)stackblock();
10395 if (psavelen > 0) {
10396 pstr = grabstackstr(pout);
10397 setinputstring(pstr);
10398 }
10399 }
10400 nlpp = &bqlist;
10401 while (*nlpp)
10402 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010403 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010404 (*nlpp)->next = NULL;
10405 parsebackquote = oldstyle;
10406
10407 if (oldstyle) {
10408 saveprompt = doprompt;
10409 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010410 }
10411
Eric Andersenc470f442003-07-28 09:56:35 +000010412 n = list(2);
10413
10414 if (oldstyle)
10415 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010416 else if (readtoken() != TRP)
10417 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010418
10419 (*nlpp)->n = n;
10420 if (oldstyle) {
10421 /*
10422 * Start reading from old file again, ignoring any pushed back
10423 * tokens left from the backquote parsing
10424 */
10425 popfile();
10426 tokpushback = 0;
10427 }
10428 while (stackblocksize() <= savelen)
10429 growstackblock();
10430 STARTSTACKSTR(out);
10431 if (str) {
10432 memcpy(out, str, savelen);
10433 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010434 INT_OFF;
10435 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010436 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010437 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010438 }
10439 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010440 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010441 if (arinest || dblquote)
10442 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10443 else
10444 USTPUTC(CTLBACKQ, out);
10445 if (oldstyle)
10446 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010447 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010448}
10449
Denis Vlasenko131ae172007-02-18 13:00:19 +000010450#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010451/*
10452 * Parse an arithmetic expansion (indicate start of one and set state)
10453 */
Eric Andersenc470f442003-07-28 09:56:35 +000010454parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010455 if (++arinest == 1) {
10456 prevsyntax = syntax;
10457 syntax = ARISYNTAX;
10458 USTPUTC(CTLARI, out);
10459 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010460 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010461 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010462 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010463 } else {
10464 /*
10465 * we collapse embedded arithmetic expansion to
10466 * parenthesis, which should be equivalent
10467 */
10468 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010469 }
Eric Andersenc470f442003-07-28 09:56:35 +000010470 goto parsearith_return;
10471}
10472#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010473
Eric Andersenc470f442003-07-28 09:56:35 +000010474} /* end of readtoken */
10475
Eric Andersencb57d552001-06-28 07:25:16 +000010476/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010477 * Read the next input token.
10478 * If the token is a word, we set backquotelist to the list of cmds in
10479 * backquotes. We set quoteflag to true if any part of the word was
10480 * quoted.
10481 * If the token is TREDIR, then we set redirnode to a structure containing
10482 * the redirection.
10483 * In all cases, the variable startlinno is set to the number of the line
10484 * on which the token starts.
10485 *
10486 * [Change comment: here documents and internal procedures]
10487 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10488 * word parsing code into a separate routine. In this case, readtoken
10489 * doesn't need to have any internal procedures, but parseword does.
10490 * We could also make parseoperator in essence the main routine, and
10491 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010492 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010493#define NEW_xxreadtoken
10494#ifdef NEW_xxreadtoken
10495/* singles must be first! */
10496static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010497
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010498static const char xxreadtoken_tokens[] = {
10499 TNL, TLP, TRP, /* only single occurrence allowed */
10500 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10501 TEOF, /* corresponds to trailing nul */
10502 TAND, TOR, TENDCASE, /* if double occurrence */
10503};
10504
10505#define xxreadtoken_doubles \
10506 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10507#define xxreadtoken_singles \
10508 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10509
10510static int
10511xxreadtoken(void)
10512{
10513 int c;
10514
10515 if (tokpushback) {
10516 tokpushback = 0;
10517 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010518 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010519 if (needprompt) {
10520 setprompt(2);
10521 }
10522 startlinno = plinno;
10523 for (;;) { /* until token or start of word found */
10524 c = pgetc_macro();
10525
10526 if ((c != ' ') && (c != '\t')
10527#if ENABLE_ASH_ALIAS
10528 && (c != PEOA)
10529#endif
10530 ) {
10531 if (c == '#') {
10532 while ((c = pgetc()) != '\n' && c != PEOF);
10533 pungetc();
10534 } else if (c == '\\') {
10535 if (pgetc() != '\n') {
10536 pungetc();
10537 goto READTOKEN1;
10538 }
10539 startlinno = ++plinno;
10540 if (doprompt)
10541 setprompt(2);
10542 } else {
10543 const char *p
10544 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10545
10546 if (c != PEOF) {
10547 if (c == '\n') {
10548 plinno++;
10549 needprompt = doprompt;
10550 }
10551
10552 p = strchr(xxreadtoken_chars, c);
10553 if (p == NULL) {
10554 READTOKEN1:
10555 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10556 }
10557
10558 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10559 if (pgetc() == *p) { /* double occurrence? */
10560 p += xxreadtoken_doubles + 1;
10561 } else {
10562 pungetc();
10563 }
10564 }
10565 }
10566 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10567 }
10568 }
10569 } /* for */
10570}
10571#else
10572#define RETURN(token) return lasttoken = token
10573static int
10574xxreadtoken(void)
10575{
10576 int c;
10577
10578 if (tokpushback) {
10579 tokpushback = 0;
10580 return lasttoken;
10581 }
10582 if (needprompt) {
10583 setprompt(2);
10584 }
10585 startlinno = plinno;
10586 for (;;) { /* until token or start of word found */
10587 c = pgetc_macro();
10588 switch (c) {
10589 case ' ': case '\t':
10590#if ENABLE_ASH_ALIAS
10591 case PEOA:
10592#endif
10593 continue;
10594 case '#':
10595 while ((c = pgetc()) != '\n' && c != PEOF);
10596 pungetc();
10597 continue;
10598 case '\\':
10599 if (pgetc() == '\n') {
10600 startlinno = ++plinno;
10601 if (doprompt)
10602 setprompt(2);
10603 continue;
10604 }
10605 pungetc();
10606 goto breakloop;
10607 case '\n':
10608 plinno++;
10609 needprompt = doprompt;
10610 RETURN(TNL);
10611 case PEOF:
10612 RETURN(TEOF);
10613 case '&':
10614 if (pgetc() == '&')
10615 RETURN(TAND);
10616 pungetc();
10617 RETURN(TBACKGND);
10618 case '|':
10619 if (pgetc() == '|')
10620 RETURN(TOR);
10621 pungetc();
10622 RETURN(TPIPE);
10623 case ';':
10624 if (pgetc() == ';')
10625 RETURN(TENDCASE);
10626 pungetc();
10627 RETURN(TSEMI);
10628 case '(':
10629 RETURN(TLP);
10630 case ')':
10631 RETURN(TRP);
10632 default:
10633 goto breakloop;
10634 }
10635 }
10636 breakloop:
10637 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10638#undef RETURN
10639}
10640#endif /* NEW_xxreadtoken */
10641
10642static int
10643readtoken(void)
10644{
10645 int t;
10646#if DEBUG
10647 int alreadyseen = tokpushback;
10648#endif
10649
10650#if ENABLE_ASH_ALIAS
10651 top:
10652#endif
10653
10654 t = xxreadtoken();
10655
10656 /*
10657 * eat newlines
10658 */
10659 if (checkkwd & CHKNL) {
10660 while (t == TNL) {
10661 parseheredoc();
10662 t = xxreadtoken();
10663 }
10664 }
10665
10666 if (t != TWORD || quoteflag) {
10667 goto out;
10668 }
10669
10670 /*
10671 * check for keywords
10672 */
10673 if (checkkwd & CHKKWD) {
10674 const char *const *pp;
10675
10676 pp = findkwd(wordtext);
10677 if (pp) {
10678 lasttoken = t = pp - tokname_array;
10679 TRACE(("keyword %s recognized\n", tokname(t)));
10680 goto out;
10681 }
10682 }
10683
10684 if (checkkwd & CHKALIAS) {
10685#if ENABLE_ASH_ALIAS
10686 struct alias *ap;
10687 ap = lookupalias(wordtext, 1);
10688 if (ap != NULL) {
10689 if (*ap->val) {
10690 pushstring(ap->val, ap);
10691 }
10692 goto top;
10693 }
10694#endif
10695 }
10696 out:
10697 checkkwd = 0;
10698#if DEBUG
10699 if (!alreadyseen)
10700 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10701 else
10702 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10703#endif
10704 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010705}
10706
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010707static char
10708peektoken(void)
10709{
10710 int t;
10711
10712 t = readtoken();
10713 tokpushback++;
10714 return tokname_array[t][0];
10715}
Eric Andersencb57d552001-06-28 07:25:16 +000010716
10717/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010718 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10719 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010720 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010721static union node *
10722parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010723{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010724 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010725
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010726 tokpushback = 0;
10727 doprompt = interact;
10728 if (doprompt)
10729 setprompt(doprompt);
10730 needprompt = 0;
10731 t = readtoken();
10732 if (t == TEOF)
10733 return NEOF;
10734 if (t == TNL)
10735 return NULL;
10736 tokpushback++;
10737 return list(1);
10738}
10739
10740/*
10741 * Input any here documents.
10742 */
10743static void
10744parseheredoc(void)
10745{
10746 struct heredoc *here;
10747 union node *n;
10748
10749 here = heredoclist;
10750 heredoclist = 0;
10751
10752 while (here) {
10753 if (needprompt) {
10754 setprompt(2);
10755 }
10756 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10757 here->eofmark, here->striptabs);
10758 n = stalloc(sizeof(struct narg));
10759 n->narg.type = NARG;
10760 n->narg.next = NULL;
10761 n->narg.text = wordtext;
10762 n->narg.backquote = backquotelist;
10763 here->here->nhere.doc = n;
10764 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010765 }
Eric Andersencb57d552001-06-28 07:25:16 +000010766}
10767
10768
10769/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010770 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010771 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010772#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010773static const char *
10774expandstr(const char *ps)
10775{
10776 union node n;
10777
10778 /* XXX Fix (char *) cast. */
10779 setinputstring((char *)ps);
10780 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10781 popfile();
10782
10783 n.narg.type = NARG;
10784 n.narg.next = NULL;
10785 n.narg.text = wordtext;
10786 n.narg.backquote = backquotelist;
10787
10788 expandarg(&n, NULL, 0);
10789 return stackblock();
10790}
10791#endif
10792
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010793/*
10794 * Execute a command or commands contained in a string.
10795 */
10796static int
10797evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010798{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010799 union node *n;
10800 struct stackmark smark;
10801 int skip;
10802
10803 setinputstring(s);
10804 setstackmark(&smark);
10805
10806 skip = 0;
10807 while ((n = parsecmd(0)) != NEOF) {
10808 evaltree(n, 0);
10809 popstackmark(&smark);
10810 skip = evalskip;
10811 if (skip)
10812 break;
10813 }
10814 popfile();
10815
10816 skip &= mask;
10817 evalskip = skip;
10818 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010819}
10820
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010821/*
10822 * The eval command.
10823 */
10824static int
10825evalcmd(int argc, char **argv)
10826{
10827 char *p;
10828 char *concat;
10829 char **ap;
10830
10831 if (argc > 1) {
10832 p = argv[1];
10833 if (argc > 2) {
10834 STARTSTACKSTR(concat);
10835 ap = argv + 2;
10836 for (;;) {
10837 concat = stack_putstr(p, concat);
10838 p = *ap++;
10839 if (p == NULL)
10840 break;
10841 STPUTC(' ', concat);
10842 }
10843 STPUTC('\0', concat);
10844 p = grabstackstr(concat);
10845 }
10846 evalstring(p, ~SKIPEVAL);
10847
10848 }
10849 return exitstatus;
10850}
10851
10852/*
10853 * Read and execute commands. "Top" is nonzero for the top level command
10854 * loop; it turns on prompting if the shell is interactive.
10855 */
10856static int
10857cmdloop(int top)
10858{
10859 union node *n;
10860 struct stackmark smark;
10861 int inter;
10862 int numeof = 0;
10863
10864 TRACE(("cmdloop(%d) called\n", top));
10865 for (;;) {
10866 int skip;
10867
10868 setstackmark(&smark);
10869#if JOBS
10870 if (jobctl)
10871 showjobs(stderr, SHOW_CHANGED);
10872#endif
10873 inter = 0;
10874 if (iflag && top) {
10875 inter++;
10876#if ENABLE_ASH_MAIL
10877 chkmail();
10878#endif
10879 }
10880 n = parsecmd(inter);
10881 /* showtree(n); DEBUG */
10882 if (n == NEOF) {
10883 if (!top || numeof >= 50)
10884 break;
10885 if (!stoppedjobs()) {
10886 if (!Iflag)
10887 break;
10888 out2str("\nUse \"exit\" to leave shell.\n");
10889 }
10890 numeof++;
10891 } else if (nflag == 0) {
10892 job_warning = (job_warning == 2) ? 1 : 0;
10893 numeof = 0;
10894 evaltree(n, 0);
10895 }
10896 popstackmark(&smark);
10897 skip = evalskip;
10898
10899 if (skip) {
10900 evalskip = 0;
10901 return skip & SKIPEVAL;
10902 }
10903 }
10904 return 0;
10905}
10906
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010907/*
10908 * Take commands from a file. To be compatible we should do a path
10909 * search for the file, which is necessary to find sub-commands.
10910 */
10911static char *
10912find_dot_file(char *name)
10913{
10914 char *fullname;
10915 const char *path = pathval();
10916 struct stat statb;
10917
10918 /* don't try this for absolute or relative paths */
10919 if (strchr(name, '/'))
10920 return name;
10921
10922 while ((fullname = padvance(&path, name)) != NULL) {
10923 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
10924 /*
10925 * Don't bother freeing here, since it will
10926 * be freed by the caller.
10927 */
10928 return fullname;
10929 }
10930 stunalloc(fullname);
10931 }
10932
10933 /* not found in the PATH */
10934 ash_msg_and_raise_error("%s: not found", name);
10935 /* NOTREACHED */
10936}
10937
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010938static int
10939dotcmd(int argc, char **argv)
10940{
10941 struct strlist *sp;
10942 volatile struct shparam saveparam;
10943 int status = 0;
10944
10945 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000010946 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010947
10948 if (argc >= 2) { /* That's what SVR2 does */
10949 char *fullname;
10950
10951 fullname = find_dot_file(argv[1]);
10952
10953 if (argc > 2) {
10954 saveparam = shellparam;
10955 shellparam.malloc = 0;
10956 shellparam.nparam = argc - 2;
10957 shellparam.p = argv + 2;
10958 };
10959
10960 setinputfile(fullname, INPUT_PUSH_FILE);
10961 commandname = fullname;
10962 cmdloop(0);
10963 popfile();
10964
10965 if (argc > 2) {
10966 freeparam(&shellparam);
10967 shellparam = saveparam;
10968 };
10969 status = exitstatus;
10970 }
10971 return status;
10972}
10973
10974static int
10975exitcmd(int argc, char **argv)
10976{
10977 if (stoppedjobs())
10978 return 0;
10979 if (argc > 1)
10980 exitstatus = number(argv[1]);
10981 raise_exception(EXEXIT);
10982 /* NOTREACHED */
10983}
10984
10985#if ENABLE_ASH_BUILTIN_ECHO
10986static int
10987echocmd(int argc, char **argv)
10988{
10989 return bb_echo(argv);
10990}
10991#endif
10992
10993#if ENABLE_ASH_BUILTIN_TEST
10994static int
10995testcmd(int argc, char **argv)
10996{
10997 return bb_test(argc, argv);
10998}
10999#endif
11000
11001/*
11002 * Read a file containing shell functions.
11003 */
11004static void
11005readcmdfile(char *name)
11006{
11007 setinputfile(name, INPUT_PUSH_FILE);
11008 cmdloop(0);
11009 popfile();
11010}
11011
11012
Denis Vlasenkocc571512007-02-23 21:10:35 +000011013/* ============ find_command inplementation */
11014
11015/*
11016 * Resolve a command name. If you change this routine, you may have to
11017 * change the shellexec routine as well.
11018 */
11019static void
11020find_command(char *name, struct cmdentry *entry, int act, const char *path)
11021{
11022 struct tblentry *cmdp;
11023 int idx;
11024 int prev;
11025 char *fullname;
11026 struct stat statb;
11027 int e;
11028 int updatetbl;
11029 struct builtincmd *bcmd;
11030
11031 /* If name contains a slash, don't use PATH or hash table */
11032 if (strchr(name, '/') != NULL) {
11033 entry->u.index = -1;
11034 if (act & DO_ABS) {
11035 while (stat(name, &statb) < 0) {
11036#ifdef SYSV
11037 if (errno == EINTR)
11038 continue;
11039#endif
11040 entry->cmdtype = CMDUNKNOWN;
11041 return;
11042 }
11043 }
11044 entry->cmdtype = CMDNORMAL;
11045 return;
11046 }
11047
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011048/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011049
11050 updatetbl = (path == pathval());
11051 if (!updatetbl) {
11052 act |= DO_ALTPATH;
11053 if (strstr(path, "%builtin") != NULL)
11054 act |= DO_ALTBLTIN;
11055 }
11056
11057 /* If name is in the table, check answer will be ok */
11058 cmdp = cmdlookup(name, 0);
11059 if (cmdp != NULL) {
11060 int bit;
11061
11062 switch (cmdp->cmdtype) {
11063 default:
11064#if DEBUG
11065 abort();
11066#endif
11067 case CMDNORMAL:
11068 bit = DO_ALTPATH;
11069 break;
11070 case CMDFUNCTION:
11071 bit = DO_NOFUNC;
11072 break;
11073 case CMDBUILTIN:
11074 bit = DO_ALTBLTIN;
11075 break;
11076 }
11077 if (act & bit) {
11078 updatetbl = 0;
11079 cmdp = NULL;
11080 } else if (cmdp->rehash == 0)
11081 /* if not invalidated by cd, we're done */
11082 goto success;
11083 }
11084
11085 /* If %builtin not in path, check for builtin next */
11086 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011087 if (bcmd) {
11088 if (IS_BUILTIN_REGULAR(bcmd))
11089 goto builtin_success;
11090 if (act & DO_ALTPATH) {
11091 if (!(act & DO_ALTBLTIN))
11092 goto builtin_success;
11093 } else if (builtinloc <= 0) {
11094 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011095 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011096 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011097
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011098#if ENABLE_FEATURE_SH_STANDALONE
11099 if (find_applet_by_name(name)) {
11100 entry->cmdtype = CMDNORMAL;
11101 entry->u.index = -1;
11102 return;
11103 }
11104#endif
11105
Denis Vlasenkocc571512007-02-23 21:10:35 +000011106 /* We have to search path. */
11107 prev = -1; /* where to start */
11108 if (cmdp && cmdp->rehash) { /* doing a rehash */
11109 if (cmdp->cmdtype == CMDBUILTIN)
11110 prev = builtinloc;
11111 else
11112 prev = cmdp->param.index;
11113 }
11114
11115 e = ENOENT;
11116 idx = -1;
11117 loop:
11118 while ((fullname = padvance(&path, name)) != NULL) {
11119 stunalloc(fullname);
11120 idx++;
11121 if (pathopt) {
11122 if (prefix(pathopt, "builtin")) {
11123 if (bcmd)
11124 goto builtin_success;
11125 continue;
11126 } else if (!(act & DO_NOFUNC) &&
11127 prefix(pathopt, "func")) {
11128 /* handled below */
11129 } else {
11130 /* ignore unimplemented options */
11131 continue;
11132 }
11133 }
11134 /* if rehash, don't redo absolute path names */
11135 if (fullname[0] == '/' && idx <= prev) {
11136 if (idx < prev)
11137 continue;
11138 TRACE(("searchexec \"%s\": no change\n", name));
11139 goto success;
11140 }
11141 while (stat(fullname, &statb) < 0) {
11142#ifdef SYSV
11143 if (errno == EINTR)
11144 continue;
11145#endif
11146 if (errno != ENOENT && errno != ENOTDIR)
11147 e = errno;
11148 goto loop;
11149 }
11150 e = EACCES; /* if we fail, this will be the error */
11151 if (!S_ISREG(statb.st_mode))
11152 continue;
11153 if (pathopt) { /* this is a %func directory */
11154 stalloc(strlen(fullname) + 1);
11155 readcmdfile(fullname);
11156 cmdp = cmdlookup(name, 0);
11157 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11158 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11159 stunalloc(fullname);
11160 goto success;
11161 }
11162 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11163 if (!updatetbl) {
11164 entry->cmdtype = CMDNORMAL;
11165 entry->u.index = idx;
11166 return;
11167 }
11168 INT_OFF;
11169 cmdp = cmdlookup(name, 1);
11170 cmdp->cmdtype = CMDNORMAL;
11171 cmdp->param.index = idx;
11172 INT_ON;
11173 goto success;
11174 }
11175
11176 /* We failed. If there was an entry for this command, delete it */
11177 if (cmdp && updatetbl)
11178 delete_cmd_entry();
11179 if (act & DO_ERR)
11180 ash_msg("%s: %s", name, errmsg(e, "not found"));
11181 entry->cmdtype = CMDUNKNOWN;
11182 return;
11183
11184 builtin_success:
11185 if (!updatetbl) {
11186 entry->cmdtype = CMDBUILTIN;
11187 entry->u.cmd = bcmd;
11188 return;
11189 }
11190 INT_OFF;
11191 cmdp = cmdlookup(name, 1);
11192 cmdp->cmdtype = CMDBUILTIN;
11193 cmdp->param.cmd = bcmd;
11194 INT_ON;
11195 success:
11196 cmdp->rehash = 0;
11197 entry->cmdtype = cmdp->cmdtype;
11198 entry->u = cmdp->param;
11199}
11200
11201
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011202/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011203
Eric Andersencb57d552001-06-28 07:25:16 +000011204/*
Eric Andersencb57d552001-06-28 07:25:16 +000011205 * The trap builtin.
11206 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011207static int
Eric Andersenc470f442003-07-28 09:56:35 +000011208trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011209{
11210 char *action;
11211 char **ap;
11212 int signo;
11213
Eric Andersenc470f442003-07-28 09:56:35 +000011214 nextopt(nullstr);
11215 ap = argptr;
11216 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011217 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011218 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011219 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011220
Rob Landleyc9c1a412006-07-12 19:17:55 +000011221 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011222 out1fmt("trap -- %s %s\n",
11223 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011224 }
11225 }
11226 return 0;
11227 }
Eric Andersenc470f442003-07-28 09:56:35 +000011228 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011229 action = NULL;
11230 else
11231 action = *ap++;
11232 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011233 signo = get_signum(*ap);
11234 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011235 ash_msg_and_raise_error("%s: bad trap", *ap);
11236 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011237 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011238 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011239 action = NULL;
11240 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011241 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011242 }
Eric Andersenc470f442003-07-28 09:56:35 +000011243 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011244 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011245 trap[signo] = action;
11246 if (signo != 0)
11247 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011248 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011249 ap++;
11250 }
11251 return 0;
11252}
11253
Eric Andersenc470f442003-07-28 09:56:35 +000011254
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011255/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011256
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011257#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011258/*
11259 * Lists available builtins
11260 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011261static int
11262helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011263{
11264 int col, i;
11265
11266 out1fmt("\nBuilt-in commands:\n-------------------\n");
11267 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11268 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011269 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011270 if (col > 60) {
11271 out1fmt("\n");
11272 col = 0;
11273 }
11274 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011275#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011276 for (i = 0; i < NUM_APPLETS; i++) {
11277 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11278 if (col > 60) {
11279 out1fmt("\n");
11280 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011281 }
11282 }
11283#endif
11284 out1fmt("\n\n");
11285 return EXIT_SUCCESS;
11286}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011287#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011288
Eric Andersencb57d552001-06-28 07:25:16 +000011289/*
Eric Andersencb57d552001-06-28 07:25:16 +000011290 * The export and readonly commands.
11291 */
Eric Andersenc470f442003-07-28 09:56:35 +000011292static int
11293exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011294{
11295 struct var *vp;
11296 char *name;
11297 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011298 char **aptr;
11299 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011300
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011301 if (nextopt("p") != 'p') {
11302 aptr = argptr;
11303 name = *aptr;
11304 if (name) {
11305 do {
11306 p = strchr(name, '=');
11307 if (p != NULL) {
11308 p++;
11309 } else {
11310 vp = *findvar(hashvar(name), name);
11311 if (vp) {
11312 vp->flags |= flag;
11313 continue;
11314 }
Eric Andersencb57d552001-06-28 07:25:16 +000011315 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011316 setvar(name, p, flag);
11317 } while ((name = *++aptr) != NULL);
11318 return 0;
11319 }
Eric Andersencb57d552001-06-28 07:25:16 +000011320 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011321 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011322 return 0;
11323}
11324
Eric Andersencb57d552001-06-28 07:25:16 +000011325/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011326 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011327 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011328static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011329unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011330{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011331 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011332
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011333 cmdp = cmdlookup(name, 0);
11334 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11335 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011336}
11337
Eric Andersencb57d552001-06-28 07:25:16 +000011338/*
Eric Andersencb57d552001-06-28 07:25:16 +000011339 * The unset builtin command. We unset the function before we unset the
11340 * variable to allow a function to be unset when there is a readonly variable
11341 * with the same name.
11342 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011343static int
Eric Andersenc470f442003-07-28 09:56:35 +000011344unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011345{
11346 char **ap;
11347 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011348 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011349 int ret = 0;
11350
11351 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011352 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011353 }
Eric Andersencb57d552001-06-28 07:25:16 +000011354
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011355 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011356 if (flag != 'f') {
11357 i = unsetvar(*ap);
11358 ret |= i;
11359 if (!(i & 2))
11360 continue;
11361 }
11362 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011363 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011364 }
Eric Andersenc470f442003-07-28 09:56:35 +000011365 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011366}
11367
11368
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011369/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011370
Eric Andersenc470f442003-07-28 09:56:35 +000011371#include <sys/times.h>
11372
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011373static const unsigned char timescmd_str[] = {
11374 ' ', offsetof(struct tms, tms_utime),
11375 '\n', offsetof(struct tms, tms_stime),
11376 ' ', offsetof(struct tms, tms_cutime),
11377 '\n', offsetof(struct tms, tms_cstime),
11378 0
11379};
Eric Andersencb57d552001-06-28 07:25:16 +000011380
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011381static int
11382timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011383{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011384 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011385 const unsigned char *p;
11386 struct tms buf;
11387
11388 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011389 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011390
11391 p = timescmd_str;
11392 do {
11393 t = *(clock_t *)(((char *) &buf) + p[1]);
11394 s = t / clk_tck;
11395 out1fmt("%ldm%ld.%.3lds%c",
11396 s/60, s%60,
11397 ((t - s * clk_tck) * 1000) / clk_tck,
11398 p[0]);
11399 } while (*(p += 2));
11400
Eric Andersencb57d552001-06-28 07:25:16 +000011401 return 0;
11402}
11403
Denis Vlasenko131ae172007-02-18 13:00:19 +000011404#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011405static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011406dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011407{
Eric Andersened9ecf72004-06-22 08:29:45 +000011408 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011409 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011410
Denis Vlasenkob012b102007-02-19 22:43:01 +000011411 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011412 result = arith(s, &errcode);
11413 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011414 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011415 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011416 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011417 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011418 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011419 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011420 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011421 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011422 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011423
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011424 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011425}
Eric Andersenc470f442003-07-28 09:56:35 +000011426
Eric Andersenc470f442003-07-28 09:56:35 +000011427/*
Eric Andersen90898442003-08-06 11:20:52 +000011428 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11429 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11430 *
11431 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011432 */
11433static int
Eric Andersen90898442003-08-06 11:20:52 +000011434letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011435{
Eric Andersenc470f442003-07-28 09:56:35 +000011436 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011437 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011438
Eric Andersen90898442003-08-06 11:20:52 +000011439 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011440 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011441 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011442 for (ap = argv + 1; *ap; ap++) {
11443 i = dash_arith(*ap);
11444 }
Eric Andersenc470f442003-07-28 09:56:35 +000011445
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011446 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011447}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011448#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011449
Eric Andersenc470f442003-07-28 09:56:35 +000011450
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011451/* ============ miscbltin.c
11452 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011453 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011454 */
11455
11456#undef rflag
11457
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011458#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011459typedef enum __rlimit_resource rlim_t;
11460#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011461
Eric Andersenc470f442003-07-28 09:56:35 +000011462/*
11463 * The read builtin. The -e option causes backslashes to escape the
11464 * following character.
11465 *
11466 * This uses unbuffered input, which may be avoidable in some cases.
11467 */
Eric Andersenc470f442003-07-28 09:56:35 +000011468static int
11469readcmd(int argc, char **argv)
11470{
11471 char **ap;
11472 int backslash;
11473 char c;
11474 int rflag;
11475 char *prompt;
11476 const char *ifs;
11477 char *p;
11478 int startword;
11479 int status;
11480 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011481#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011482 int nch_flag = 0;
11483 int nchars = 0;
11484 int silent = 0;
11485 struct termios tty, old_tty;
11486#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011487#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011488 fd_set set;
11489 struct timeval ts;
11490
11491 ts.tv_sec = ts.tv_usec = 0;
11492#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011493
11494 rflag = 0;
11495 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011496#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011497 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011498#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011499 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011500#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011501 while ((i = nextopt("p:rt:")) != '\0')
11502#else
11503 while ((i = nextopt("p:r")) != '\0')
11504#endif
11505 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011506 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011507 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011508 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011509 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011510#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011511 case 'n':
11512 nchars = strtol(optionarg, &p, 10);
11513 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011514 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011515 nch_flag = (nchars > 0);
11516 break;
11517 case 's':
11518 silent = 1;
11519 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011520#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011521#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011522 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011523 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011524 ts.tv_usec = 0;
11525 if (*p == '.') {
11526 char *p2;
11527 if (*++p) {
11528 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000011529 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011530 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011531 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011532 scale = p2 - p;
11533 /* normalize to usec */
11534 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011535 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011536 while (scale++ < 6)
11537 ts.tv_usec *= 10;
11538 }
11539 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011540 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011541 }
11542 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011543 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011544 break;
11545#endif
11546 case 'r':
11547 rflag = 1;
11548 break;
11549 default:
11550 break;
11551 }
Eric Andersenc470f442003-07-28 09:56:35 +000011552 }
11553 if (prompt && isatty(0)) {
11554 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011555 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011556 ap = argptr;
11557 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011558 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011559 ifs = bltinlookup("IFS");
11560 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011561 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011562#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011563 if (nch_flag || silent) {
11564 tcgetattr(0, &tty);
11565 old_tty = tty;
11566 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011567 tty.c_lflag &= ~ICANON;
11568 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000011569 }
11570 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011571 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000011572
11573 }
11574 tcsetattr(0, TCSANOW, &tty);
11575 }
11576#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011577#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011578 if (ts.tv_sec || ts.tv_usec) {
11579 FD_ZERO (&set);
11580 FD_SET (0, &set);
11581
Denis Vlasenkof4dff772006-12-24 07:14:17 +000011582 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000011583 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011584#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011585 if (nch_flag)
11586 tcsetattr(0, TCSANOW, &old_tty);
11587#endif
11588 return 1;
11589 }
11590 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011591#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011592 status = 0;
11593 startword = 1;
11594 backslash = 0;
11595 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000011596#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011597 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000011598#else
11599 for (;;)
11600#endif
11601 {
Eric Andersenc470f442003-07-28 09:56:35 +000011602 if (read(0, &c, 1) != 1) {
11603 status = 1;
11604 break;
11605 }
11606 if (c == '\0')
11607 continue;
11608 if (backslash) {
11609 backslash = 0;
11610 if (c != '\n')
11611 goto put;
11612 continue;
11613 }
11614 if (!rflag && c == '\\') {
11615 backslash++;
11616 continue;
11617 }
11618 if (c == '\n')
11619 break;
11620 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11621 continue;
11622 }
11623 startword = 0;
11624 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11625 STACKSTRNUL(p);
11626 setvar(*ap, stackblock(), 0);
11627 ap++;
11628 startword = 1;
11629 STARTSTACKSTR(p);
11630 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011631 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011632 STPUTC(c, p);
11633 }
11634 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000011635#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011636 if (nch_flag || silent)
11637 tcsetattr(0, TCSANOW, &old_tty);
11638#endif
11639
Eric Andersenc470f442003-07-28 09:56:35 +000011640 STACKSTRNUL(p);
11641 /* Remove trailing blanks */
11642 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11643 *p = '\0';
11644 setvar(*ap, stackblock(), 0);
11645 while (*++ap != NULL)
11646 setvar(*ap, nullstr, 0);
11647 return status;
11648}
11649
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011650static int
11651umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011652{
11653 static const char permuser[3] = "ugo";
11654 static const char permmode[3] = "rwx";
11655 static const short int permmask[] = {
11656 S_IRUSR, S_IWUSR, S_IXUSR,
11657 S_IRGRP, S_IWGRP, S_IXGRP,
11658 S_IROTH, S_IWOTH, S_IXOTH
11659 };
11660
11661 char *ap;
11662 mode_t mask;
11663 int i;
11664 int symbolic_mode = 0;
11665
11666 while (nextopt("S") != '\0') {
11667 symbolic_mode = 1;
11668 }
11669
Denis Vlasenkob012b102007-02-19 22:43:01 +000011670 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011671 mask = umask(0);
11672 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011673 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011674
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011675 ap = *argptr;
11676 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011677 if (symbolic_mode) {
11678 char buf[18];
11679 char *p = buf;
11680
11681 for (i = 0; i < 3; i++) {
11682 int j;
11683
11684 *p++ = permuser[i];
11685 *p++ = '=';
11686 for (j = 0; j < 3; j++) {
11687 if ((mask & permmask[3 * i + j]) == 0) {
11688 *p++ = permmode[j];
11689 }
11690 }
11691 *p++ = ',';
11692 }
11693 *--p = 0;
11694 puts(buf);
11695 } else {
11696 out1fmt("%.4o\n", mask);
11697 }
11698 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011699 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011700 mask = 0;
11701 do {
11702 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011703 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011704 mask = (mask << 3) + (*ap - '0');
11705 } while (*++ap != '\0');
11706 umask(mask);
11707 } else {
11708 mask = ~mask & 0777;
11709 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011710 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011711 }
11712 umask(~mask & 0777);
11713 }
11714 }
11715 return 0;
11716}
11717
11718/*
11719 * ulimit builtin
11720 *
11721 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11722 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11723 * ash by J.T. Conklin.
11724 *
11725 * Public domain.
11726 */
11727
11728struct limits {
11729 const char *name;
11730 int cmd;
11731 int factor; /* multiply by to get rlim_{cur,max} values */
11732 char option;
11733};
11734
11735static const struct limits limits[] = {
11736#ifdef RLIMIT_CPU
11737 { "time(seconds)", RLIMIT_CPU, 1, 't' },
11738#endif
11739#ifdef RLIMIT_FSIZE
11740 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
11741#endif
11742#ifdef RLIMIT_DATA
11743 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
11744#endif
11745#ifdef RLIMIT_STACK
11746 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
11747#endif
11748#ifdef RLIMIT_CORE
11749 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
11750#endif
11751#ifdef RLIMIT_RSS
11752 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
11753#endif
11754#ifdef RLIMIT_MEMLOCK
11755 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
11756#endif
11757#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000011758 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011759#endif
11760#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000011761 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011762#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011763#ifdef RLIMIT_AS
11764 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011765#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011766#ifdef RLIMIT_LOCKS
11767 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011768#endif
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011769 { NULL, 0, 0, '\0' }
Eric Andersenc470f442003-07-28 09:56:35 +000011770};
11771
Glenn L McGrath76620622004-01-13 10:19:37 +000011772enum limtype { SOFT = 0x1, HARD = 0x2 };
11773
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011774static void
11775printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011776 const struct limits *l)
11777{
11778 rlim_t val;
11779
11780 val = limit->rlim_max;
11781 if (how & SOFT)
11782 val = limit->rlim_cur;
11783
11784 if (val == RLIM_INFINITY)
11785 out1fmt("unlimited\n");
11786 else {
11787 val /= l->factor;
11788 out1fmt("%lld\n", (long long) val);
11789 }
11790}
11791
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011792static int
Eric Andersenc470f442003-07-28 09:56:35 +000011793ulimitcmd(int argc, char **argv)
11794{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011795 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000011796 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000011797 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011798 const struct limits *l;
11799 int set, all = 0;
11800 int optc, what;
11801 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000011802
11803 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000011804 while ((optc = nextopt("HSa"
11805#ifdef RLIMIT_CPU
11806 "t"
11807#endif
11808#ifdef RLIMIT_FSIZE
11809 "f"
11810#endif
11811#ifdef RLIMIT_DATA
11812 "d"
11813#endif
11814#ifdef RLIMIT_STACK
11815 "s"
11816#endif
11817#ifdef RLIMIT_CORE
11818 "c"
11819#endif
11820#ifdef RLIMIT_RSS
11821 "m"
11822#endif
11823#ifdef RLIMIT_MEMLOCK
11824 "l"
11825#endif
11826#ifdef RLIMIT_NPROC
11827 "p"
11828#endif
11829#ifdef RLIMIT_NOFILE
11830 "n"
11831#endif
11832#ifdef RLIMIT_AS
11833 "v"
11834#endif
11835#ifdef RLIMIT_LOCKS
11836 "w"
11837#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011838 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000011839 switch (optc) {
11840 case 'H':
11841 how = HARD;
11842 break;
11843 case 'S':
11844 how = SOFT;
11845 break;
11846 case 'a':
11847 all = 1;
11848 break;
11849 default:
11850 what = optc;
11851 }
11852
Glenn L McGrath76620622004-01-13 10:19:37 +000011853 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000011854 ;
Eric Andersenc470f442003-07-28 09:56:35 +000011855
11856 set = *argptr ? 1 : 0;
11857 if (set) {
11858 char *p = *argptr;
11859
11860 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011861 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000011862 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000011863 val = RLIM_INFINITY;
11864 else {
11865 val = (rlim_t) 0;
11866
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011867 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000011868 val = (val * 10) + (long)(c - '0');
11869 if (val < (rlim_t) 0)
11870 break;
11871 }
11872 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011873 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000011874 val *= l->factor;
11875 }
11876 }
11877 if (all) {
11878 for (l = limits; l->name; l++) {
11879 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000011880 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000011881 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011882 }
11883 return 0;
11884 }
11885
11886 getrlimit(l->cmd, &limit);
11887 if (set) {
11888 if (how & HARD)
11889 limit.rlim_max = val;
11890 if (how & SOFT)
11891 limit.rlim_cur = val;
11892 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011893 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000011894 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000011895 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000011896 }
11897 return 0;
11898}
11899
Eric Andersen90898442003-08-06 11:20:52 +000011900
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011901/* ============ Math support */
11902
Denis Vlasenko131ae172007-02-18 13:00:19 +000011903#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000011904
11905/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
11906
11907 Permission is hereby granted, free of charge, to any person obtaining
11908 a copy of this software and associated documentation files (the
11909 "Software"), to deal in the Software without restriction, including
11910 without limitation the rights to use, copy, modify, merge, publish,
11911 distribute, sublicense, and/or sell copies of the Software, and to
11912 permit persons to whom the Software is furnished to do so, subject to
11913 the following conditions:
11914
11915 The above copyright notice and this permission notice shall be
11916 included in all copies or substantial portions of the Software.
11917
11918 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11919 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11920 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11921 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
11922 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
11923 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
11924 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11925*/
11926
11927/* This is my infix parser/evaluator. It is optimized for size, intended
11928 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000011929 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000011930 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000011931 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000011932 * be that which POSIX specifies for shells. */
11933
11934/* The code uses a simple two-stack algorithm. See
11935 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000011936 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000011937 * this is based (this code differs in that it applies operators immediately
11938 * to the stack instead of adding them to a queue to end up with an
11939 * expression). */
11940
11941/* To use the routine, call it with an expression string and error return
11942 * pointer */
11943
11944/*
11945 * Aug 24, 2001 Manuel Novoa III
11946 *
11947 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
11948 *
11949 * 1) In arith_apply():
11950 * a) Cached values of *numptr and &(numptr[-1]).
11951 * b) Removed redundant test for zero denominator.
11952 *
11953 * 2) In arith():
11954 * a) Eliminated redundant code for processing operator tokens by moving
11955 * to a table-based implementation. Also folded handling of parens
11956 * into the table.
11957 * b) Combined all 3 loops which called arith_apply to reduce generated
11958 * code size at the cost of speed.
11959 *
11960 * 3) The following expressions were treated as valid by the original code:
11961 * 1() , 0! , 1 ( *3 ) .
11962 * These bugs have been fixed by internally enclosing the expression in
11963 * parens and then checking that all binary ops and right parens are
11964 * preceded by a valid expression (NUM_TOKEN).
11965 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011966 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000011967 * ctype's isspace() if it is used by another busybox applet or if additional
11968 * whitespace chars should be considered. Look below the "#include"s for a
11969 * precompiler test.
11970 */
11971
11972/*
11973 * Aug 26, 2001 Manuel Novoa III
11974 *
11975 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
11976 *
11977 * Merge in Aaron's comments previously posted to the busybox list,
11978 * modified slightly to take account of my changes to the code.
11979 *
11980 */
11981
11982/*
11983 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
11984 *
11985 * - allow access to variable,
11986 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
11987 * - realize assign syntax (VAR=expr, +=, *= etc)
11988 * - realize exponentiation (** operator)
11989 * - realize comma separated - expr, expr
11990 * - realise ++expr --expr expr++ expr--
11991 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000011992 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000011993 * - was restored loses XOR operator
11994 * - remove one goto label, added three ;-)
11995 * - protect $((num num)) as true zero expr (Manuel`s error)
11996 * - always use special isspace(), see comment from bash ;-)
11997 */
11998
Eric Andersen90898442003-08-06 11:20:52 +000011999#define arith_isspace(arithval) \
12000 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12001
Eric Andersen90898442003-08-06 11:20:52 +000012002typedef unsigned char operator;
12003
12004/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012005 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012006 * precedence. The ID portion is so that multiple operators can have the
12007 * same precedence, ensuring that the leftmost one is evaluated first.
12008 * Consider * and /. */
12009
12010#define tok_decl(prec,id) (((id)<<5)|(prec))
12011#define PREC(op) ((op) & 0x1F)
12012
12013#define TOK_LPAREN tok_decl(0,0)
12014
12015#define TOK_COMMA tok_decl(1,0)
12016
12017#define TOK_ASSIGN tok_decl(2,0)
12018#define TOK_AND_ASSIGN tok_decl(2,1)
12019#define TOK_OR_ASSIGN tok_decl(2,2)
12020#define TOK_XOR_ASSIGN tok_decl(2,3)
12021#define TOK_PLUS_ASSIGN tok_decl(2,4)
12022#define TOK_MINUS_ASSIGN tok_decl(2,5)
12023#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12024#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12025
12026#define TOK_MUL_ASSIGN tok_decl(3,0)
12027#define TOK_DIV_ASSIGN tok_decl(3,1)
12028#define TOK_REM_ASSIGN tok_decl(3,2)
12029
12030/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012031#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012032
12033/* conditional is right associativity too */
12034#define TOK_CONDITIONAL tok_decl(4,0)
12035#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12036
12037#define TOK_OR tok_decl(5,0)
12038
12039#define TOK_AND tok_decl(6,0)
12040
12041#define TOK_BOR tok_decl(7,0)
12042
12043#define TOK_BXOR tok_decl(8,0)
12044
12045#define TOK_BAND tok_decl(9,0)
12046
12047#define TOK_EQ tok_decl(10,0)
12048#define TOK_NE tok_decl(10,1)
12049
12050#define TOK_LT tok_decl(11,0)
12051#define TOK_GT tok_decl(11,1)
12052#define TOK_GE tok_decl(11,2)
12053#define TOK_LE tok_decl(11,3)
12054
12055#define TOK_LSHIFT tok_decl(12,0)
12056#define TOK_RSHIFT tok_decl(12,1)
12057
12058#define TOK_ADD tok_decl(13,0)
12059#define TOK_SUB tok_decl(13,1)
12060
12061#define TOK_MUL tok_decl(14,0)
12062#define TOK_DIV tok_decl(14,1)
12063#define TOK_REM tok_decl(14,2)
12064
12065/* exponent is right associativity */
12066#define TOK_EXPONENT tok_decl(15,1)
12067
12068/* For now unary operators. */
12069#define UNARYPREC 16
12070#define TOK_BNOT tok_decl(UNARYPREC,0)
12071#define TOK_NOT tok_decl(UNARYPREC,1)
12072
12073#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12074#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12075
12076#define PREC_PRE (UNARYPREC+2)
12077
12078#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12079#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12080
12081#define PREC_POST (UNARYPREC+3)
12082
12083#define TOK_POST_INC tok_decl(PREC_POST, 0)
12084#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12085
12086#define SPEC_PREC (UNARYPREC+4)
12087
12088#define TOK_NUM tok_decl(SPEC_PREC, 0)
12089#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12090
12091#define NUMPTR (*numstackptr)
12092
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012093static int
12094tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012095{
12096 operator prec = PREC(op);
12097
12098 convert_prec_is_assing(prec);
12099 return (prec == PREC(TOK_ASSIGN) ||
12100 prec == PREC_PRE || prec == PREC_POST);
12101}
12102
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012103static int
12104is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012105{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012106 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12107 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012108}
12109
Eric Andersen90898442003-08-06 11:20:52 +000012110typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012111 arith_t val;
12112 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012113 char contidional_second_val_initialized;
12114 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012115 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012116} v_n_t;
12117
Eric Andersen90898442003-08-06 11:20:52 +000012118typedef struct CHK_VAR_RECURSIVE_LOOPED {
12119 const char *var;
12120 struct CHK_VAR_RECURSIVE_LOOPED *next;
12121} chk_var_recursive_looped_t;
12122
12123static chk_var_recursive_looped_t *prev_chk_var_recursive;
12124
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012125static int
12126arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012127{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012128 if (t->var) {
12129 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012130
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012131 if (p) {
12132 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012133
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012134 /* recursive try as expression */
12135 chk_var_recursive_looped_t *cur;
12136 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012137
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012138 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12139 if (strcmp(cur->var, t->var) == 0) {
12140 /* expression recursion loop detected */
12141 return -5;
12142 }
12143 }
12144 /* save current lookuped var name */
12145 cur = prev_chk_var_recursive;
12146 cur_save.var = t->var;
12147 cur_save.next = cur;
12148 prev_chk_var_recursive = &cur_save;
12149
12150 t->val = arith (p, &errcode);
12151 /* restore previous ptr after recursiving */
12152 prev_chk_var_recursive = cur;
12153 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012154 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012155 /* allow undefined var as 0 */
12156 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012157 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012158 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012159}
12160
12161/* "applying" a token means performing it on the top elements on the integer
12162 * stack. For a unary operator it will only change the top element, but a
12163 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012164static int
12165arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012166{
Eric Andersen90898442003-08-06 11:20:52 +000012167 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012168 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012169 int ret_arith_lookup_val;
12170
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012171 /* There is no operator that can work without arguments */
12172 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012173 numptr_m1 = NUMPTR - 1;
12174
12175 /* check operand is var with noninteger value */
12176 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012177 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012178 return ret_arith_lookup_val;
12179
12180 rez = numptr_m1->val;
12181 if (op == TOK_UMINUS)
12182 rez *= -1;
12183 else if (op == TOK_NOT)
12184 rez = !rez;
12185 else if (op == TOK_BNOT)
12186 rez = ~rez;
12187 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12188 rez++;
12189 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12190 rez--;
12191 else if (op != TOK_UPLUS) {
12192 /* Binary operators */
12193
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012194 /* check and binary operators need two arguments */
12195 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012196
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012197 /* ... and they pop one */
12198 --NUMPTR;
12199 numptr_val = rez;
12200 if (op == TOK_CONDITIONAL) {
12201 if (! numptr_m1->contidional_second_val_initialized) {
12202 /* protect $((expr1 ? expr2)) without ": expr" */
12203 goto err;
12204 }
12205 rez = numptr_m1->contidional_second_val;
12206 } else if (numptr_m1->contidional_second_val_initialized) {
12207 /* protect $((expr1 : expr2)) without "expr ? " */
12208 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012209 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012210 numptr_m1 = NUMPTR - 1;
12211 if (op != TOK_ASSIGN) {
12212 /* check operand is var with noninteger value for not '=' */
12213 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12214 if (ret_arith_lookup_val)
12215 return ret_arith_lookup_val;
12216 }
12217 if (op == TOK_CONDITIONAL) {
12218 numptr_m1->contidional_second_val = rez;
12219 }
12220 rez = numptr_m1->val;
12221 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012222 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012223 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012224 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012225 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012226 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012227 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012228 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012229 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012230 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012231 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012232 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012233 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012234 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012235 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012236 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012237 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012238 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012239 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012240 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012241 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012242 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012243 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012244 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012245 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012246 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012247 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012248 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012249 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012250 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012251 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012252 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012253 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012254 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012255 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012256 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012257 /* protect $((expr : expr)) without "expr ? " */
12258 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012259 }
12260 numptr_m1->contidional_second_val_initialized = op;
12261 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012262 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012263 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012264 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012265 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012266 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012267 return -3; /* exponent less than 0 */
12268 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012269 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012270
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012271 if (numptr_val)
12272 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012273 c *= rez;
12274 rez = c;
12275 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012276 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012277 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012278 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012279 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012280 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012281 rez %= numptr_val;
12282 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012283 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012284 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012285
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012286 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012287 /* Hmm, 1=2 ? */
12288 goto err;
12289 }
12290 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012291#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012292 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012293#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012294 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012295#endif
Eric Andersen90898442003-08-06 11:20:52 +000012296 setvar(numptr_m1->var, buf, 0);
12297 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012298 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012299 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012300 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012301 rez++;
12302 }
12303 numptr_m1->val = rez;
12304 /* protect geting var value, is number now */
12305 numptr_m1->var = NULL;
12306 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012307 err:
12308 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012309}
12310
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012311/* longest must be first */
Eric Andersen90898442003-08-06 11:20:52 +000012312static const char op_tokens[] = {
12313 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12314 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12315 '<','<', 0, TOK_LSHIFT,
12316 '>','>', 0, TOK_RSHIFT,
12317 '|','|', 0, TOK_OR,
12318 '&','&', 0, TOK_AND,
12319 '!','=', 0, TOK_NE,
12320 '<','=', 0, TOK_LE,
12321 '>','=', 0, TOK_GE,
12322 '=','=', 0, TOK_EQ,
12323 '|','=', 0, TOK_OR_ASSIGN,
12324 '&','=', 0, TOK_AND_ASSIGN,
12325 '*','=', 0, TOK_MUL_ASSIGN,
12326 '/','=', 0, TOK_DIV_ASSIGN,
12327 '%','=', 0, TOK_REM_ASSIGN,
12328 '+','=', 0, TOK_PLUS_ASSIGN,
12329 '-','=', 0, TOK_MINUS_ASSIGN,
12330 '-','-', 0, TOK_POST_DEC,
12331 '^','=', 0, TOK_XOR_ASSIGN,
12332 '+','+', 0, TOK_POST_INC,
12333 '*','*', 0, TOK_EXPONENT,
12334 '!', 0, TOK_NOT,
12335 '<', 0, TOK_LT,
12336 '>', 0, TOK_GT,
12337 '=', 0, TOK_ASSIGN,
12338 '|', 0, TOK_BOR,
12339 '&', 0, TOK_BAND,
12340 '*', 0, TOK_MUL,
12341 '/', 0, TOK_DIV,
12342 '%', 0, TOK_REM,
12343 '+', 0, TOK_ADD,
12344 '-', 0, TOK_SUB,
12345 '^', 0, TOK_BXOR,
12346 /* uniq */
12347 '~', 0, TOK_BNOT,
12348 ',', 0, TOK_COMMA,
12349 '?', 0, TOK_CONDITIONAL,
12350 ':', 0, TOK_CONDITIONAL_SEP,
12351 ')', 0, TOK_RPAREN,
12352 '(', 0, TOK_LPAREN,
12353 0
12354};
12355/* ptr to ")" */
12356#define endexpression &op_tokens[sizeof(op_tokens)-7]
12357
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012358static arith_t
12359arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012360{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012361 char arithval; /* Current character under analysis */
12362 operator lasttok, op;
12363 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012364
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012365 const char *p = endexpression;
12366 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012367
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012368 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012369
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012370 /* Stack of integers */
12371 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12372 * in any given correct or incorrect expression is left as an exercise to
12373 * the reader. */
12374 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12375 *numstackptr = numstack;
12376 /* Stack of operator tokens */
12377 operator *stack = alloca((datasizes) * sizeof(operator)),
12378 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012379
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012380 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12381 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012382
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012383 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012384 arithval = *expr;
12385 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 if (p == endexpression) {
12387 /* Null expression. */
12388 return 0;
12389 }
12390
12391 /* This is only reached after all tokens have been extracted from the
12392 * input stream. If there are still tokens on the operator stack, they
12393 * are to be applied in order. At the end, there should be a final
12394 * result on the integer stack */
12395
12396 if (expr != endexpression + 1) {
12397 /* If we haven't done so already, */
12398 /* append a closing right paren */
12399 expr = endexpression;
12400 /* and let the loop process it. */
12401 continue;
12402 }
12403 /* At this point, we're done with the expression. */
12404 if (numstackptr != numstack+1) {
12405 /* ... but if there isn't, it's bad */
12406 err:
12407 return (*perrcode = -1);
12408 }
12409 if (numstack->var) {
12410 /* expression is $((var)) only, lookup now */
12411 errcode = arith_lookup_val(numstack);
12412 }
12413 ret:
12414 *perrcode = errcode;
12415 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012416 }
12417
Eric Andersen90898442003-08-06 11:20:52 +000012418 /* Continue processing the expression. */
12419 if (arith_isspace(arithval)) {
12420 /* Skip whitespace */
12421 goto prologue;
12422 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012423 p = endofname(expr);
12424 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012425 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012426
12427 numstackptr->var = alloca(var_name_size);
12428 safe_strncpy(numstackptr->var, expr, var_name_size);
12429 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012430 num:
Eric Andersen90898442003-08-06 11:20:52 +000012431 numstackptr->contidional_second_val_initialized = 0;
12432 numstackptr++;
12433 lasttok = TOK_NUM;
12434 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012435 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012436 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012437 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012438#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012439 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012440#else
12441 numstackptr->val = strtol(expr, (char **) &expr, 0);
12442#endif
Eric Andersen90898442003-08-06 11:20:52 +000012443 goto num;
12444 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012446 const char *o;
12447
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012448 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012449 /* strange operator not found */
12450 goto err;
12451 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012452 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012453 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012454 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012455 /* found */
12456 expr = o - 1;
12457 break;
12458 }
12459 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012460 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012461 p++;
12462 /* skip zero delim */
12463 p++;
12464 }
12465 op = p[1];
12466
12467 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012468 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12469 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012470
12471 /* Plus and minus are binary (not unary) _only_ if the last
12472 * token was as number, or a right paren (which pretends to be
12473 * a number, since it evaluates to one). Think about it.
12474 * It makes sense. */
12475 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012476 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012477 case TOK_ADD:
12478 op = TOK_UPLUS;
12479 break;
12480 case TOK_SUB:
12481 op = TOK_UMINUS;
12482 break;
12483 case TOK_POST_INC:
12484 op = TOK_PRE_INC;
12485 break;
12486 case TOK_POST_DEC:
12487 op = TOK_PRE_DEC;
12488 break;
Eric Andersen90898442003-08-06 11:20:52 +000012489 }
12490 }
12491 /* We don't want a unary operator to cause recursive descent on the
12492 * stack, because there can be many in a row and it could cause an
12493 * operator to be evaluated before its argument is pushed onto the
12494 * integer stack. */
12495 /* But for binary operators, "apply" everything on the operator
12496 * stack until we find an operator with a lesser priority than the
12497 * one we have just extracted. */
12498 /* Left paren is given the lowest priority so it will never be
12499 * "applied" in this way.
12500 * if associativity is right and priority eq, applied also skip
12501 */
12502 prec = PREC(op);
12503 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12504 /* not left paren or unary */
12505 if (lasttok != TOK_NUM) {
12506 /* binary op must be preceded by a num */
12507 goto err;
12508 }
12509 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012510 if (op == TOK_RPAREN) {
12511 /* The algorithm employed here is simple: while we don't
12512 * hit an open paren nor the bottom of the stack, pop
12513 * tokens and apply them */
12514 if (stackptr[-1] == TOK_LPAREN) {
12515 --stackptr;
12516 /* Any operator directly after a */
12517 lasttok = TOK_NUM;
12518 /* close paren should consider itself binary */
12519 goto prologue;
12520 }
12521 } else {
12522 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012523
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012524 convert_prec_is_assing(prec);
12525 convert_prec_is_assing(prev_prec);
12526 if (prev_prec < prec)
12527 break;
12528 /* check right assoc */
12529 if (prev_prec == prec && is_right_associativity(prec))
12530 break;
12531 }
12532 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12533 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012534 }
12535 if (op == TOK_RPAREN) {
12536 goto err;
12537 }
12538 }
12539
12540 /* Push this operator to the stack and remember it. */
12541 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012542 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012543 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012544 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012545}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012546#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012547
12548
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012549/* ============ main() and helpers */
12550
12551/*
12552 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012553 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012554static void exitshell(void) ATTRIBUTE_NORETURN;
12555static void
12556exitshell(void)
12557{
12558 struct jmploc loc;
12559 char *p;
12560 int status;
12561
12562 status = exitstatus;
12563 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12564 if (setjmp(loc.loc)) {
12565 if (exception == EXEXIT)
12566/* dash bug: it just does _exit(exitstatus) here
12567 * but we have to do setjobctl(0) first!
12568 * (bug is still not fixed in dash-0.5.3 - if you run dash
12569 * under Midnight Commander, on exit from dash MC is backgrounded) */
12570 status = exitstatus;
12571 goto out;
12572 }
12573 exception_handler = &loc;
12574 p = trap[0];
12575 if (p) {
12576 trap[0] = NULL;
12577 evalstring(p, 0);
12578 }
12579 flush_stdout_stderr();
12580 out:
12581 setjobctl(0);
12582 _exit(status);
12583 /* NOTREACHED */
12584}
12585
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012586static void
12587init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012588{
12589 /* from input.c: */
12590 basepf.nextc = basepf.buf = basebuf;
12591
12592 /* from trap.c: */
12593 signal(SIGCHLD, SIG_DFL);
12594
12595 /* from var.c: */
12596 {
12597 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012598 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012599 const char *p;
12600 struct stat st1, st2;
12601
12602 initvar();
12603 for (envp = environ; envp && *envp; envp++) {
12604 if (strchr(*envp, '=')) {
12605 setvareq(*envp, VEXPORT|VTEXTFIXED);
12606 }
12607 }
12608
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012609 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012610 setvar("PPID", ppid, 0);
12611
12612 p = lookupvar("PWD");
12613 if (p)
12614 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12615 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12616 p = '\0';
12617 setpwd(p, 0);
12618 }
12619}
12620
12621/*
12622 * Process the shell command line arguments.
12623 */
12624static void
12625procargs(int argc, char **argv)
12626{
12627 int i;
12628 const char *xminusc;
12629 char **xargv;
12630
12631 xargv = argv;
12632 arg0 = xargv[0];
12633 if (argc > 0)
12634 xargv++;
12635 for (i = 0; i < NOPTS; i++)
12636 optlist[i] = 2;
12637 argptr = xargv;
12638 options(1);
12639 xargv = argptr;
12640 xminusc = minusc;
12641 if (*xargv == NULL) {
12642 if (xminusc)
12643 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12644 sflag = 1;
12645 }
12646 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12647 iflag = 1;
12648 if (mflag == 2)
12649 mflag = iflag;
12650 for (i = 0; i < NOPTS; i++)
12651 if (optlist[i] == 2)
12652 optlist[i] = 0;
12653#if DEBUG == 2
12654 debug = 1;
12655#endif
12656 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12657 if (xminusc) {
12658 minusc = *xargv++;
12659 if (*xargv)
12660 goto setarg0;
12661 } else if (!sflag) {
12662 setinputfile(*xargv, 0);
12663 setarg0:
12664 arg0 = *xargv++;
12665 commandname = arg0;
12666 }
12667
12668 shellparam.p = xargv;
12669#if ENABLE_ASH_GETOPTS
12670 shellparam.optind = 1;
12671 shellparam.optoff = -1;
12672#endif
12673 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
12674 while (*xargv) {
12675 shellparam.nparam++;
12676 xargv++;
12677 }
12678 optschanged();
12679}
12680
12681/*
12682 * Read /etc/profile or .profile.
12683 */
12684static void
12685read_profile(const char *name)
12686{
12687 int skip;
12688
12689 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12690 return;
12691 skip = cmdloop(0);
12692 popfile();
12693 if (skip)
12694 exitshell();
12695}
12696
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012697/*
12698 * This routine is called when an error or an interrupt occurs in an
12699 * interactive shell and control is returned to the main command loop.
12700 */
12701static void
12702reset(void)
12703{
12704 /* from eval.c: */
12705 evalskip = 0;
12706 loopnest = 0;
12707 /* from input.c: */
12708 parselleft = parsenleft = 0; /* clear input buffer */
12709 popallfiles();
12710 /* from parser.c: */
12711 tokpushback = 0;
12712 checkkwd = 0;
12713 /* from redir.c: */
12714 clearredir(0);
12715}
12716
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012717#if PROFILE
12718static short profile_buf[16384];
12719extern int etext();
12720#endif
12721
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012722/*
12723 * Main routine. We initialize things, parse the arguments, execute
12724 * profiles if we're a login shell, and then call cmdloop to execute
12725 * commands. The setjmp call sets up the location to jump to when an
12726 * exception occurs. When an exception occurs the variable "state"
12727 * is used to figure out how far we had gotten.
12728 */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012729int ash_main(int argc, char **argv);
12730int ash_main(int argc, char **argv)
12731{
12732 char *shinit;
12733 volatile int state;
12734 struct jmploc jmploc;
12735 struct stackmark smark;
12736
12737#ifdef __GLIBC__
12738 dash_errno = __errno_location();
12739#endif
12740
12741#if PROFILE
12742 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12743#endif
12744
12745#if ENABLE_FEATURE_EDITING
12746 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12747#endif
12748 state = 0;
12749 if (setjmp(jmploc.loc)) {
12750 int e;
12751 int s;
12752
12753 reset();
12754
12755 e = exception;
12756 if (e == EXERROR)
12757 exitstatus = 2;
12758 s = state;
12759 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12760 exitshell();
12761
12762 if (e == EXINT) {
12763 outcslow('\n', stderr);
12764 }
12765 popstackmark(&smark);
12766 FORCE_INT_ON; /* enable interrupts */
12767 if (s == 1)
12768 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012769 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012770 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012771 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012772 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012773 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012774 }
12775 exception_handler = &jmploc;
12776#if DEBUG
12777 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012778 trace_puts("Shell args: ");
12779 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012780#endif
12781 rootpid = getpid();
12782
12783#if ENABLE_ASH_RANDOM_SUPPORT
12784 rseed = rootpid + time(NULL);
12785#endif
12786 init();
12787 setstackmark(&smark);
12788 procargs(argc, argv);
12789#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12790 if (iflag) {
12791 const char *hp = lookupvar("HISTFILE");
12792
12793 if (hp == NULL) {
12794 hp = lookupvar("HOME");
12795 if (hp != NULL) {
12796 char *defhp = concat_path_file(hp, ".ash_history");
12797 setvar("HISTFILE", defhp, 0);
12798 free(defhp);
12799 }
12800 }
12801 }
12802#endif
12803 if (argv[0] && argv[0][0] == '-')
12804 isloginsh = 1;
12805 if (isloginsh) {
12806 state = 1;
12807 read_profile("/etc/profile");
12808 state1:
12809 state = 2;
12810 read_profile(".profile");
12811 }
12812 state2:
12813 state = 3;
12814 if (
12815#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012816 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012817#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012818 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012819 ) {
12820 shinit = lookupvar("ENV");
12821 if (shinit != NULL && *shinit != '\0') {
12822 read_profile(shinit);
12823 }
12824 }
12825 state3:
12826 state = 4;
12827 if (minusc)
12828 evalstring(minusc, 0);
12829
12830 if (sflag || minusc == NULL) {
12831#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12832 if ( iflag ) {
12833 const char *hp = lookupvar("HISTFILE");
12834
12835 if (hp != NULL)
12836 line_input_state->hist_file = hp;
12837 }
12838#endif
12839 state4: /* XXX ??? - why isn't this before the "if" statement */
12840 cmdloop(1);
12841 }
12842#if PROFILE
12843 monitor(0);
12844#endif
12845#ifdef GPROF
12846 {
12847 extern void _mcleanup(void);
12848 _mcleanup();
12849 }
12850#endif
12851 exitshell();
12852 /* NOTREACHED */
12853}
12854
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000012855#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000012856const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000012857int main(int argc, char **argv)
12858{
12859 return ash_main(argc, argv);
12860}
12861#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012862
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012863
Eric Andersendf82f612001-06-28 07:46:40 +000012864/*-
12865 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012866 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012867 *
12868 * This code is derived from software contributed to Berkeley by
12869 * Kenneth Almquist.
12870 *
12871 * Redistribution and use in source and binary forms, with or without
12872 * modification, are permitted provided that the following conditions
12873 * are met:
12874 * 1. Redistributions of source code must retain the above copyright
12875 * notice, this list of conditions and the following disclaimer.
12876 * 2. Redistributions in binary form must reproduce the above copyright
12877 * notice, this list of conditions and the following disclaimer in the
12878 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000012879 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000012880 * may be used to endorse or promote products derived from this software
12881 * without specific prior written permission.
12882 *
12883 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12884 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12885 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12886 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12887 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12888 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12889 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12890 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12891 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12892 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12893 * SUCH DAMAGE.
12894 */